mirror of
https://github.com/denoland/deno.git
synced 2024-11-26 16:09:27 -05:00
4c779b5e8c
This ports the REPL over to Rust and makes use of an inspector session to run a REPL on top of any isolate which lets make full use of rustylines various things like validators and completors without having to introduce a bunch of hard to test internal ops and glue code. An accidental but good side effect of this is that the multiple line input we previously had is now an editable multi-line input prompt that is correctly stored in the history as a single entry.
390 lines
13 KiB
JavaScript
390 lines
13 KiB
JavaScript
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
// Removes the `__proto__` for security reasons. This intentionally makes
|
|
// Deno non compliant with ECMA-262 Annex B.2.2.1
|
|
//
|
|
delete Object.prototype.__proto__;
|
|
|
|
((window) => {
|
|
const core = Deno.core;
|
|
const util = window.__bootstrap.util;
|
|
const eventTarget = window.__bootstrap.eventTarget;
|
|
const dispatchMinimal = window.__bootstrap.dispatchMinimal;
|
|
const build = window.__bootstrap.build;
|
|
const version = window.__bootstrap.version;
|
|
const errorStack = window.__bootstrap.errorStack;
|
|
const os = window.__bootstrap.os;
|
|
const timers = window.__bootstrap.timers;
|
|
const Console = window.__bootstrap.console.Console;
|
|
const worker = window.__bootstrap.worker;
|
|
const signals = window.__bootstrap.signals;
|
|
const { internalSymbol, internalObject } = window.__bootstrap.internals;
|
|
const performance = window.__bootstrap.performance;
|
|
const crypto = window.__bootstrap.crypto;
|
|
const url = window.__bootstrap.url;
|
|
const headers = window.__bootstrap.headers;
|
|
const streams = window.__bootstrap.streams;
|
|
const fileReader = window.__bootstrap.fileReader;
|
|
const webSocket = window.__bootstrap.webSocket;
|
|
const fetch = window.__bootstrap.fetch;
|
|
const denoNs = window.__bootstrap.denoNs;
|
|
const denoNsUnstable = window.__bootstrap.denoNsUnstable;
|
|
const errors = window.__bootstrap.errors.errors;
|
|
|
|
let windowIsClosing = false;
|
|
|
|
function windowClose() {
|
|
if (!windowIsClosing) {
|
|
windowIsClosing = true;
|
|
// Push a macrotask to exit after a promise resolve.
|
|
// This is not perfect, but should be fine for first pass.
|
|
Promise.resolve().then(() =>
|
|
timers.setTimeout.call(
|
|
null,
|
|
() => {
|
|
// This should be fine, since only Window/MainWorker has .close()
|
|
os.exit(0);
|
|
},
|
|
0,
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
const encoder = new TextEncoder();
|
|
|
|
function workerClose() {
|
|
if (isClosing) {
|
|
return;
|
|
}
|
|
|
|
isClosing = true;
|
|
opCloseWorker();
|
|
}
|
|
|
|
// TODO(bartlomieju): remove these funtions
|
|
// Stuff for workers
|
|
const onmessage = () => {};
|
|
const onerror = () => {};
|
|
|
|
function postMessage(data) {
|
|
const dataJson = JSON.stringify(data);
|
|
const dataIntArray = encoder.encode(dataJson);
|
|
opPostMessage(dataIntArray);
|
|
}
|
|
|
|
let isClosing = false;
|
|
async function workerMessageRecvCallback(data) {
|
|
const msgEvent = new MessageEvent("message", {
|
|
cancelable: false,
|
|
data,
|
|
});
|
|
|
|
try {
|
|
if (globalThis["onmessage"]) {
|
|
const result = globalThis.onmessage(msgEvent);
|
|
if (result && "then" in result) {
|
|
await result;
|
|
}
|
|
}
|
|
globalThis.dispatchEvent(msgEvent);
|
|
} catch (e) {
|
|
let handled = false;
|
|
|
|
const errorEvent = new ErrorEvent("error", {
|
|
cancelable: true,
|
|
message: e.message,
|
|
lineno: e.lineNumber ? e.lineNumber + 1 : undefined,
|
|
colno: e.columnNumber ? e.columnNumber + 1 : undefined,
|
|
filename: e.fileName,
|
|
error: null,
|
|
});
|
|
|
|
if (globalThis["onerror"]) {
|
|
const ret = globalThis.onerror(
|
|
e.message,
|
|
e.fileName,
|
|
e.lineNumber,
|
|
e.columnNumber,
|
|
e,
|
|
);
|
|
handled = ret === true;
|
|
}
|
|
|
|
globalThis.dispatchEvent(errorEvent);
|
|
if (errorEvent.defaultPrevented) {
|
|
handled = true;
|
|
}
|
|
|
|
if (!handled) {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
function opPostMessage(data) {
|
|
core.jsonOpSync("op_worker_post_message", {}, data);
|
|
}
|
|
|
|
function opCloseWorker() {
|
|
core.jsonOpSync("op_worker_close");
|
|
}
|
|
|
|
function opStart() {
|
|
return core.jsonOpSync("op_start");
|
|
}
|
|
|
|
function opMainModule() {
|
|
return core.jsonOpSync("op_main_module");
|
|
}
|
|
|
|
// TODO(bartlomieju): temporary solution, must be fixed when moving
|
|
// dispatches to separate crates
|
|
function initOps() {
|
|
const opsMap = core.ops();
|
|
for (const [name, opId] of Object.entries(opsMap)) {
|
|
if (name === "op_write" || name === "op_read") {
|
|
core.setAsyncHandler(opId, dispatchMinimal.asyncMsgFromRust);
|
|
}
|
|
}
|
|
core.setMacrotaskCallback(timers.handleTimerMacrotask);
|
|
}
|
|
|
|
function runtimeStart(source) {
|
|
initOps();
|
|
// First we send an empty `Start` message to let the privileged side know we
|
|
// are ready. The response should be a `StartRes` message containing the CLI
|
|
// args and other info.
|
|
const s = opStart();
|
|
version.setVersions(s.denoVersion, s.v8Version, s.tsVersion);
|
|
build.setBuildInfo(s.target);
|
|
util.setLogDebug(s.debugFlag, source);
|
|
errorStack.setPrepareStackTrace(Error);
|
|
return s;
|
|
}
|
|
|
|
function registerErrors() {
|
|
core.registerErrorClass("NotFound", errors.NotFound);
|
|
core.registerErrorClass("PermissionDenied", errors.PermissionDenied);
|
|
core.registerErrorClass("ConnectionRefused", errors.ConnectionRefused);
|
|
core.registerErrorClass("ConnectionReset", errors.ConnectionReset);
|
|
core.registerErrorClass("ConnectionAborted", errors.ConnectionAborted);
|
|
core.registerErrorClass("NotConnected", errors.NotConnected);
|
|
core.registerErrorClass("AddrInUse", errors.AddrInUse);
|
|
core.registerErrorClass("AddrNotAvailable", errors.AddrNotAvailable);
|
|
core.registerErrorClass("BrokenPipe", errors.BrokenPipe);
|
|
core.registerErrorClass("AlreadyExists", errors.AlreadyExists);
|
|
core.registerErrorClass("InvalidData", errors.InvalidData);
|
|
core.registerErrorClass("TimedOut", errors.TimedOut);
|
|
core.registerErrorClass("Interrupted", errors.Interrupted);
|
|
core.registerErrorClass("WriteZero", errors.WriteZero);
|
|
core.registerErrorClass("UnexpectedEof", errors.UnexpectedEof);
|
|
core.registerErrorClass("BadResource", errors.BadResource);
|
|
core.registerErrorClass("Http", errors.Http);
|
|
core.registerErrorClass("Busy", errors.Busy);
|
|
core.registerErrorClass("NotSupported", errors.NotSupported);
|
|
core.registerErrorClass("Error", Error);
|
|
core.registerErrorClass("RangeError", RangeError);
|
|
core.registerErrorClass("ReferenceError", ReferenceError);
|
|
core.registerErrorClass("SyntaxError", SyntaxError);
|
|
core.registerErrorClass("TypeError", TypeError);
|
|
core.registerErrorClass("URIError", URIError);
|
|
}
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
|
|
const windowOrWorkerGlobalScope = {
|
|
Blob: util.nonEnumerable(fetch.Blob),
|
|
ByteLengthQueuingStrategy: util.nonEnumerable(
|
|
streams.ByteLengthQueuingStrategy,
|
|
),
|
|
CloseEvent: util.nonEnumerable(CloseEvent),
|
|
CountQueuingStrategy: util.nonEnumerable(
|
|
streams.CountQueuingStrategy,
|
|
),
|
|
CustomEvent: util.nonEnumerable(CustomEvent),
|
|
DOMException: util.nonEnumerable(DOMException),
|
|
ErrorEvent: util.nonEnumerable(ErrorEvent),
|
|
Event: util.nonEnumerable(Event),
|
|
EventTarget: util.nonEnumerable(EventTarget),
|
|
File: util.nonEnumerable(fetch.DomFile),
|
|
FileReader: util.nonEnumerable(fileReader.FileReader),
|
|
FormData: util.nonEnumerable(fetch.FormData),
|
|
Headers: util.nonEnumerable(headers.Headers),
|
|
MessageEvent: util.nonEnumerable(MessageEvent),
|
|
Performance: util.nonEnumerable(performance.Performance),
|
|
PerformanceEntry: util.nonEnumerable(performance.PerformanceEntry),
|
|
PerformanceMark: util.nonEnumerable(performance.PerformanceMark),
|
|
PerformanceMeasure: util.nonEnumerable(performance.PerformanceMeasure),
|
|
ProgressEvent: util.nonEnumerable(ProgressEvent),
|
|
ReadableStream: util.nonEnumerable(streams.ReadableStream),
|
|
Request: util.nonEnumerable(fetch.Request),
|
|
Response: util.nonEnumerable(fetch.Response),
|
|
TextDecoder: util.nonEnumerable(TextDecoder),
|
|
TextEncoder: util.nonEnumerable(TextEncoder),
|
|
TransformStream: util.nonEnumerable(streams.TransformStream),
|
|
URL: util.nonEnumerable(url.URL),
|
|
URLSearchParams: util.nonEnumerable(url.URLSearchParams),
|
|
WebSocket: util.nonEnumerable(webSocket.WebSocket),
|
|
Worker: util.nonEnumerable(worker.Worker),
|
|
WritableStream: util.nonEnumerable(streams.WritableStream),
|
|
addEventListener: util.readOnly(EventTarget.prototype.addEventListener),
|
|
atob: util.writable(atob),
|
|
btoa: util.writable(btoa),
|
|
clearInterval: util.writable(timers.clearInterval),
|
|
clearTimeout: util.writable(timers.clearTimeout),
|
|
console: util.writable(new Console(core.print)),
|
|
crypto: util.readOnly(crypto),
|
|
dispatchEvent: util.readOnly(EventTarget.prototype.dispatchEvent),
|
|
fetch: util.writable(fetch.fetch),
|
|
performance: util.writable(performance.performance),
|
|
removeEventListener: util.readOnly(
|
|
EventTarget.prototype.removeEventListener,
|
|
),
|
|
setInterval: util.writable(timers.setInterval),
|
|
setTimeout: util.writable(timers.setTimeout),
|
|
};
|
|
|
|
const mainRuntimeGlobalProperties = {
|
|
window: util.readOnly(globalThis),
|
|
self: util.readOnly(globalThis),
|
|
// TODO(bartlomieju): from MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope)
|
|
// it seems those two properties should be available to workers as well
|
|
onload: util.writable(null),
|
|
onunload: util.writable(null),
|
|
close: util.writable(windowClose),
|
|
closed: util.getterOnly(() => windowIsClosing),
|
|
};
|
|
|
|
const workerRuntimeGlobalProperties = {
|
|
self: util.readOnly(globalThis),
|
|
onmessage: util.writable(onmessage),
|
|
onerror: util.writable(onerror),
|
|
// TODO: should be readonly?
|
|
close: util.nonEnumerable(workerClose),
|
|
postMessage: util.writable(postMessage),
|
|
workerMessageRecvCallback: util.nonEnumerable(workerMessageRecvCallback),
|
|
};
|
|
|
|
let hasBootstrapped = false;
|
|
|
|
function bootstrapMainRuntime() {
|
|
if (hasBootstrapped) {
|
|
throw new Error("Worker runtime already bootstrapped");
|
|
}
|
|
// Remove bootstrapping data from the global scope
|
|
delete globalThis.__bootstrap;
|
|
delete globalThis.bootstrap;
|
|
util.log("bootstrapMainRuntime");
|
|
hasBootstrapped = true;
|
|
Object.defineProperties(globalThis, windowOrWorkerGlobalScope);
|
|
Object.defineProperties(globalThis, mainRuntimeGlobalProperties);
|
|
eventTarget.setEventTargetData(globalThis);
|
|
// Registers the handler for window.onload function.
|
|
globalThis.addEventListener("load", (e) => {
|
|
const { onload } = globalThis;
|
|
if (typeof onload === "function") {
|
|
onload(e);
|
|
}
|
|
});
|
|
// Registers the handler for window.onunload function.
|
|
globalThis.addEventListener("unload", (e) => {
|
|
const { onunload } = globalThis;
|
|
if (typeof onunload === "function") {
|
|
onunload(e);
|
|
}
|
|
});
|
|
|
|
const { args, cwd, noColor, pid, ppid, unstableFlag } = runtimeStart();
|
|
|
|
registerErrors();
|
|
|
|
const finalDenoNs = {
|
|
core,
|
|
internal: internalSymbol,
|
|
[internalSymbol]: internalObject,
|
|
resources: core.resources,
|
|
close: core.close,
|
|
...denoNs,
|
|
};
|
|
Object.defineProperties(finalDenoNs, {
|
|
pid: util.readOnly(pid),
|
|
ppid: util.readOnly(ppid),
|
|
noColor: util.readOnly(noColor),
|
|
args: util.readOnly(Object.freeze(args)),
|
|
mainModule: util.getterOnly(opMainModule),
|
|
});
|
|
|
|
if (unstableFlag) {
|
|
Object.assign(finalDenoNs, denoNsUnstable);
|
|
}
|
|
|
|
// Setup `Deno` global - we're actually overriding already
|
|
// existing global `Deno` with `Deno` namespace from "./deno.ts".
|
|
util.immutableDefine(globalThis, "Deno", finalDenoNs);
|
|
Object.freeze(globalThis.Deno);
|
|
Object.freeze(globalThis.Deno.core);
|
|
Object.freeze(globalThis.Deno.core.sharedQueue);
|
|
signals.setSignals();
|
|
|
|
util.log("cwd", cwd);
|
|
util.log("args", args);
|
|
}
|
|
|
|
function bootstrapWorkerRuntime(name, useDenoNamespace, internalName) {
|
|
if (hasBootstrapped) {
|
|
throw new Error("Worker runtime already bootstrapped");
|
|
}
|
|
// Remove bootstrapping data from the global scope
|
|
delete globalThis.__bootstrap;
|
|
delete globalThis.bootstrap;
|
|
util.log("bootstrapWorkerRuntime");
|
|
hasBootstrapped = true;
|
|
Object.defineProperties(globalThis, windowOrWorkerGlobalScope);
|
|
Object.defineProperties(globalThis, workerRuntimeGlobalProperties);
|
|
Object.defineProperties(globalThis, { name: util.readOnly(name) });
|
|
eventTarget.setEventTargetData(globalThis);
|
|
const { unstableFlag, pid, noColor, args } = runtimeStart(
|
|
internalName ?? name,
|
|
);
|
|
|
|
registerErrors();
|
|
|
|
const finalDenoNs = {
|
|
core,
|
|
internal: internalSymbol,
|
|
[internalSymbol]: internalObject,
|
|
resources: core.resources,
|
|
close: core.close,
|
|
...denoNs,
|
|
};
|
|
if (useDenoNamespace) {
|
|
if (unstableFlag) {
|
|
Object.assign(finalDenoNs, denoNsUnstable);
|
|
}
|
|
Object.defineProperties(finalDenoNs, {
|
|
pid: util.readOnly(pid),
|
|
noColor: util.readOnly(noColor),
|
|
args: util.readOnly(Object.freeze(args)),
|
|
});
|
|
// Setup `Deno` global - we're actually overriding already
|
|
// existing global `Deno` with `Deno` namespace from "./deno.ts".
|
|
util.immutableDefine(globalThis, "Deno", finalDenoNs);
|
|
Object.freeze(globalThis.Deno);
|
|
Object.freeze(globalThis.Deno.core);
|
|
Object.freeze(globalThis.Deno.core.sharedQueue);
|
|
signals.setSignals();
|
|
} else {
|
|
delete globalThis.Deno;
|
|
util.assert(globalThis.Deno === undefined);
|
|
}
|
|
}
|
|
|
|
Object.defineProperties(globalThis, {
|
|
bootstrap: {
|
|
value: {
|
|
mainRuntime: bootstrapMainRuntime,
|
|
workerRuntime: bootstrapWorkerRuntime,
|
|
},
|
|
configurable: true,
|
|
},
|
|
});
|
|
})(this);
|