@dodona/papyros
Version:
Scratchpad for multiple programming languages in the browser.
148 lines • 5.7 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Backend } from "../../Backend";
import { BackendEventType } from "../../BackendEvent";
/**
* Implementation of a JavaScript backend for Papyros
* by using eval and overriding some builtins
*/
export class JavaScriptWorker extends Backend {
/**
* Convert varargs to a string, similar to how the console does it
* @param {any[]} args The values to join into a string
* @return {string} The string representation
*/
static stringify(...args) {
const asString = args.map(a => {
if (Array.isArray(a)) {
return JSON.stringify(a);
}
else if (typeof (a) === "string") {
return a;
}
else if (typeof a === "number") {
return a + "";
}
else if (typeof (a) === "object" && "toString" in a) {
let aString = a.toString();
if (aString === "[object Object]") { // useless toString, so use JSON
aString = JSON.stringify(a);
}
return aString;
}
else {
return JSON.stringify(args);
}
}).join(" ");
return asString;
}
/**
* Prompt the user for input with a message
* @param {string} text The message to show when asking for input
* @return {string} The value the user gave
*/
prompt(text = "") {
return this.onEvent({
type: BackendEventType.Input,
data: text,
contentType: "text/plain"
});
}
/**
* Print values to the output screen
* @param {any[]} args The values to log
*/
consoleLog(...args) {
this.queue.put(BackendEventType.Output, JavaScriptWorker.stringify(...args) + "\n", "text/plain");
}
/**
* Print values to the error screen
* @param {any[]} args The error values to log
*/
consoleError(...args) {
this.queue.put(BackendEventType.Error, JavaScriptWorker.stringify(...args) + "\n", "text/plain");
}
/**
* Helper method to generate suggestions based on properties in an object
* @param {number} from Where in the document the autocompletion starts
* @param {any} object Object with properties that might be relevant
* @return {CompletionResult} Autocompletion suggestions
*/
static completeProperties(from, object) {
const options = Object.keys(object).map(name => {
return {
label: name,
type: typeof object[name] === "function" ? "function" : "variable"
};
});
return {
from,
options,
validFor: /^[\w$]*$/
};
}
runCode(extras, code) {
return __awaiter(this, void 0, void 0, function* () {
this.extras = extras;
this.queue.reset();
// Builtins to store before execution and restore afterwards
// Workers do not have access to prompt
const oldContent = {
"console.log": console.log,
"console.error": console.error
};
// Overrides for the builtins
const newContext = {
"prompt": this.prompt.bind(this),
"console.log": this.consoleLog.bind(this),
"console.error": this.consoleError.bind(this)
};
// Override the builtins
new Function("ctx", Object.keys(newContext).map(k => `${k} = ctx['${k}'];`).join("\n"))(newContext);
let result = null;
try { // run the user's code
this.onEvent({
type: BackendEventType.Start,
contentType: "text/plain",
data: "RunCode"
});
result = yield eval(code);
}
catch (error) { // try to create a friendly traceback
Error.captureStackTrace(error);
result = yield this.onEvent({
type: BackendEventType.Error,
contentType: "application/json",
data: {
name: error.constructor.name,
what: error.message,
traceback: error.stack
}
});
}
finally { // restore the old builtins
new Function("ctx", Object.keys(oldContent).map(k => `${k} = ctx['${k}'];`).join("\n"))(oldContent);
this.queue.flush();
this.onEvent({
type: BackendEventType.End,
contentType: "text/plain",
data: "CodeFinished"
});
}
return result;
});
}
lintCode() {
return __awaiter(this, void 0, void 0, function* () {
return Promise.resolve([]);
});
}
}
//# sourceMappingURL=JavaScriptWorker.js.map