UNPKG

@dodona/papyros

Version:

Scratchpad for multiple programming languages in the browser.

148 lines 5.7 kB
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