mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
perf(core): async op pseudo-codegen and performance work (#18887)
Performance: ``` async_ops.js: 760k -> 1030k (!) async_ops_deferred.js: 730k -> 770k Deno.serve bench: 118k -> 124k WS test w/ third_party/prebuilt/mac/load_test 100 localhost 8000 0 0: unchanged Startup time: approx 0.5ms slower (13.7 -> 14.2ms) ```
This commit is contained in:
parent
9c8ebce3dc
commit
bb1f5e4262
45 changed files with 737 additions and 237 deletions
|
@ -17,4 +17,6 @@ async function bench(fun) {
|
|||
}
|
||||
|
||||
const core = Deno[Deno.internal].core;
|
||||
bench(() => core.opAsync("op_void_async"));
|
||||
const ops = core.ops;
|
||||
const opVoidAsync = ops.op_void_async;
|
||||
bench(() => opVoidAsync());
|
||||
|
|
|
@ -17,4 +17,6 @@ async function bench(fun) {
|
|||
}
|
||||
|
||||
const core = Deno[Deno.internal].core;
|
||||
bench(() => core.opAsync("op_void_async_deferred"));
|
||||
const ops = core.ops;
|
||||
const opVoidAsyncDeferred = ops.op_void_async_deferred;
|
||||
bench(() => opVoidAsyncDeferred());
|
||||
|
|
|
@ -80,12 +80,14 @@ Deno.test(function metricsForOpCrates() {
|
|||
// Test that op_names == Objects.keys(Deno[Deno.internal].core.ops)
|
||||
// since building the per-op metrics depends on op_names being complete
|
||||
Deno.test(function opNamesMatch() {
|
||||
// @ts-ignore: Deno[Deno.internal].core allowed
|
||||
const ops = Object.keys(Deno[Deno.internal].core.ops);
|
||||
// @ts-ignore: Deno[Deno.internal].core allowed
|
||||
ops.concat(Object.keys(Deno[Deno.internal].core.asyncOps));
|
||||
|
||||
assertEquals(
|
||||
// @ts-ignore: Deno[Deno.internal].core allowed
|
||||
Deno[Deno.internal].core.opNames().sort(),
|
||||
// @ts-ignore: Deno[Deno.internal].core allowed
|
||||
Object.keys(Deno[Deno.internal].core.ops).sort().filter((name) =>
|
||||
name !== "asyncOpsInfo"
|
||||
),
|
||||
ops.sort().filter((name) => name !== "asyncOpsInfo"),
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { assertEquals } from "https://deno.land/std@v0.42.0/testing/asserts.ts";
|
||||
import { assert, assertStringIncludes, unreachable } from "./test_util.ts";
|
||||
|
||||
Deno.test(async function sendAsyncStackTrace() {
|
||||
const buf = new Uint8Array(10);
|
||||
const rid = 10;
|
||||
try {
|
||||
await Deno.read(rid, buf);
|
||||
await core.ops.op_error_async();
|
||||
unreachable();
|
||||
} catch (error) {
|
||||
assert(error instanceof Error);
|
||||
const s = error.stack?.toString();
|
||||
assert(s);
|
||||
console.log(s);
|
||||
assertStringIncludes(s, "opcall_test.ts");
|
||||
assertStringIncludes(s, "read");
|
||||
assertStringIncludes(s, "sendAsyncStackTrace");
|
||||
assert(
|
||||
!s.includes("ext:core"),
|
||||
"opcall stack traces should NOT include ext:core internals such as unwrapOpResult",
|
||||
|
@ -22,6 +20,31 @@ Deno.test(async function sendAsyncStackTrace() {
|
|||
}
|
||||
});
|
||||
|
||||
Deno.test(async function sendAsyncStackTraceDeferred() {
|
||||
try {
|
||||
await core.ops.op_error_async_deferred();
|
||||
unreachable();
|
||||
} catch (error) {
|
||||
assert(error instanceof Error);
|
||||
const s = error.stack?.toString();
|
||||
assert(s);
|
||||
assertStringIncludes(s, "opcall_test.ts");
|
||||
assertStringIncludes(s, "sendAsyncStackTraceDeferred");
|
||||
assert(
|
||||
!s.includes("ext:core"),
|
||||
"opcall stack traces should NOT include ext:core internals such as unwrapOpResult",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test(function syncAdd() {
|
||||
assertEquals(30, core.ops.op_add(10, 20));
|
||||
});
|
||||
|
||||
Deno.test(async function asyncAdd() {
|
||||
assertEquals(30, await core.ops.op_add_async(10, 20));
|
||||
});
|
||||
|
||||
// @ts-ignore This is not publicly typed namespace, but it's there for sure.
|
||||
const core = Deno[Deno.internal].core;
|
||||
|
||||
|
|
2
cli/tsc/compiler.d.ts
vendored
2
cli/tsc/compiler.d.ts
vendored
|
@ -46,6 +46,8 @@ declare global {
|
|||
encode(value: string): Uint8Array;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
ops: Record<string, (...args: unknown[]) => any>;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
asyncOps: Record<string, (...args: unknown[]) => any>;
|
||||
print(msg: string, stderr: boolean): void;
|
||||
registerErrorClass(
|
||||
name: string,
|
||||
|
|
469
core/01_core.js
469
core/01_core.js
|
@ -16,11 +16,15 @@
|
|||
ObjectAssign,
|
||||
ObjectFreeze,
|
||||
ObjectFromEntries,
|
||||
ObjectKeys,
|
||||
Promise,
|
||||
PromiseReject,
|
||||
PromiseResolve,
|
||||
PromisePrototypeThen,
|
||||
RangeError,
|
||||
ReferenceError,
|
||||
ReflectHas,
|
||||
ReflectApply,
|
||||
SafeArrayIterator,
|
||||
SafeMap,
|
||||
SafePromisePrototypeFinally,
|
||||
|
@ -32,7 +36,7 @@
|
|||
TypeError,
|
||||
URIError,
|
||||
} = window.__bootstrap.primordials;
|
||||
const { ops } = window.Deno.core;
|
||||
const { ops, asyncOps } = window.Deno.core;
|
||||
|
||||
const build = {
|
||||
target: "unknown",
|
||||
|
@ -85,6 +89,17 @@
|
|||
return opCallTracingEnabled;
|
||||
}
|
||||
|
||||
function movePromise(promiseId) {
|
||||
const idx = promiseId % RING_SIZE;
|
||||
// Move old promise from ring to map
|
||||
const oldPromise = promiseRing[idx];
|
||||
if (oldPromise !== NO_PROMISE) {
|
||||
const oldPromiseId = promiseId - RING_SIZE;
|
||||
MapPrototypeSet(promiseMap, oldPromiseId, oldPromise);
|
||||
}
|
||||
return promiseRing[idx] = NO_PROMISE;
|
||||
}
|
||||
|
||||
function setPromise(promiseId) {
|
||||
const idx = promiseId % RING_SIZE;
|
||||
// Move old promise from ring to map
|
||||
|
@ -208,7 +223,29 @@
|
|||
return error;
|
||||
}
|
||||
|
||||
function unwrapOpResult(res) {
|
||||
function unwrapOpError(hideFunction) {
|
||||
return (res) => {
|
||||
// .$err_class_name is a special key that should only exist on errors
|
||||
const className = res?.$err_class_name;
|
||||
if (!className) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const errorBuilder = errorMap[className];
|
||||
const err = errorBuilder ? errorBuilder(res.message) : new Error(
|
||||
`Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`,
|
||||
);
|
||||
// Set .code if error was a known OS error, see error_codes.rs
|
||||
if (res.code) {
|
||||
err.code = res.code;
|
||||
}
|
||||
// Strip unwrapOpResult() and errorBuilder() calls from stack trace
|
||||
ErrorCaptureStackTrace(err, hideFunction);
|
||||
throw err;
|
||||
};
|
||||
}
|
||||
|
||||
function unwrapOpResultNewPromise(id, res, hideFunction) {
|
||||
// .$err_class_name is a special key that should only exist on errors
|
||||
if (res?.$err_class_name) {
|
||||
const className = res.$err_class_name;
|
||||
|
@ -221,59 +258,359 @@
|
|||
err.code = res.code;
|
||||
}
|
||||
// Strip unwrapOpResult() and errorBuilder() calls from stack trace
|
||||
ErrorCaptureStackTrace(err, unwrapOpResult);
|
||||
throw err;
|
||||
ErrorCaptureStackTrace(err, hideFunction);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
return res;
|
||||
const promise = PromiseResolve(res);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
}
|
||||
|
||||
/*
|
||||
Basic codegen.
|
||||
|
||||
TODO(mmastrac): automate this (handlebars?)
|
||||
|
||||
let s = "";
|
||||
const vars = "abcdefghijklm";
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let args = "";
|
||||
for (let j = 0; j < i; j++) {
|
||||
args += `${vars[j]},`;
|
||||
}
|
||||
s += `
|
||||
case ${i}:
|
||||
fn = function async_op_${i}(${args}) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, ${args});
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_${i});
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_${i});
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(setPromise(id), unwrapOpError(eventLoopTick));
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
`;
|
||||
}
|
||||
*/
|
||||
|
||||
// This function is called once per async stub
|
||||
function asyncStub(opName, args) {
|
||||
setUpAsyncStub(opName);
|
||||
return ReflectApply(ops[opName], undefined, args);
|
||||
}
|
||||
|
||||
function setUpAsyncStub(opName) {
|
||||
const originalOp = asyncOps[opName];
|
||||
let fn;
|
||||
// The body of this switch statement can be generated using the script above.
|
||||
switch (originalOp.length - 1) {
|
||||
case 0:
|
||||
fn = function async_op_0() {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_0);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_0);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
case 1:
|
||||
fn = function async_op_1(a) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, a);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_1);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_1);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
case 2:
|
||||
fn = function async_op_2(a, b) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, a, b);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_2);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_2);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
case 3:
|
||||
fn = function async_op_3(a, b, c) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, a, b, c);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_3);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_3);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
case 4:
|
||||
fn = function async_op_4(a, b, c, d) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, a, b, c, d);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_4);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_4);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
case 5:
|
||||
fn = function async_op_5(a, b, c, d, e) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, a, b, c, d, e);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_5);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_5);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
case 6:
|
||||
fn = function async_op_6(a, b, c, d, e, f) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, a, b, c, d, e, f);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_6);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_6);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
case 7:
|
||||
fn = function async_op_7(a, b, c, d, e, f, g) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, a, b, c, d, e, f, g);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_7);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_7);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
case 8:
|
||||
fn = function async_op_8(a, b, c, d, e, f, g, h) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, a, b, c, d, e, f, g, h);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_8);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_8);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
case 9:
|
||||
fn = function async_op_9(a, b, c, d, e, f, g, h, i) {
|
||||
const id = nextPromiseId++;
|
||||
try {
|
||||
const maybeResult = originalOp(id, a, b, c, d, e, f, g, h, i);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, async_op_9);
|
||||
}
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
ErrorCaptureStackTrace(err, async_op_9);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(opName, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
return promise;
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(
|
||||
`Too many arguments for async op codegen (length of ${opName} was ${
|
||||
originalOp.length - 1
|
||||
})`,
|
||||
);
|
||||
}
|
||||
return (ops[opName] = fn);
|
||||
}
|
||||
|
||||
function opAsync2(name, arg0, arg1) {
|
||||
const id = nextPromiseId++;
|
||||
let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult);
|
||||
let maybeResult;
|
||||
try {
|
||||
maybeResult = ops[name](id, arg0, arg1);
|
||||
} catch (err) {
|
||||
// Cleanup the just-created promise
|
||||
getPromise(id);
|
||||
if (!ReflectHas(ops, name)) {
|
||||
throw new TypeError(`${name} is not a registered op`);
|
||||
const maybeResult = asyncOps[name](id, arg0, arg1);
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, opAsync2);
|
||||
}
|
||||
// Rethrow the error
|
||||
throw err;
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
if (!ReflectHas(asyncOps, name)) {
|
||||
return PromiseReject(new TypeError(`${name} is not a registered op`));
|
||||
}
|
||||
ErrorCaptureStackTrace(err, opAsync2);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(name, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
if (typeof maybeResult !== "undefined") {
|
||||
const promise = getPromise(id);
|
||||
promise.resolve(maybeResult);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function opAsync(name, ...args) {
|
||||
const id = nextPromiseId++;
|
||||
let promise = PromisePrototypeThen(setPromise(id), unwrapOpResult);
|
||||
let maybeResult;
|
||||
try {
|
||||
maybeResult = ops[name](id, ...new SafeArrayIterator(args));
|
||||
} catch (err) {
|
||||
// Cleanup the just-created promise
|
||||
getPromise(id);
|
||||
if (!ReflectHas(ops, name)) {
|
||||
throw new TypeError(`${name} is not a registered op`);
|
||||
const maybeResult = asyncOps[name](id, ...new SafeArrayIterator(args));
|
||||
if (maybeResult !== undefined) {
|
||||
movePromise(id);
|
||||
return unwrapOpResultNewPromise(id, maybeResult, opAsync);
|
||||
}
|
||||
// Rethrow the error
|
||||
throw err;
|
||||
} catch (err) {
|
||||
movePromise(id);
|
||||
if (!ReflectHas(asyncOps, name)) {
|
||||
return PromiseReject(new TypeError(`${name} is not a registered op`));
|
||||
}
|
||||
ErrorCaptureStackTrace(err, opAsync);
|
||||
return PromiseReject(err);
|
||||
}
|
||||
let promise = PromisePrototypeThen(
|
||||
setPromise(id),
|
||||
unwrapOpError(eventLoopTick),
|
||||
);
|
||||
promise = handleOpCallTracing(name, id, promise);
|
||||
promise[promiseIdSymbol] = id;
|
||||
if (typeof maybeResult !== "undefined") {
|
||||
const promise = getPromise(id);
|
||||
promise.resolve(maybeResult);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
@ -439,8 +776,52 @@
|
|||
);
|
||||
}
|
||||
|
||||
// Eagerly initialize ops for snapshot purposes
|
||||
for (const opName of new SafeArrayIterator(ObjectKeys(asyncOps))) {
|
||||
setUpAsyncStub(opName);
|
||||
}
|
||||
|
||||
function generateAsyncOpHandler(/* opNames... */) {
|
||||
const fastOps = {};
|
||||
for (const opName of new SafeArrayIterator(arguments)) {
|
||||
if (ops[opName] === undefined) {
|
||||
throw new Error(`Unknown or disabled op '${opName}'`);
|
||||
}
|
||||
if (asyncOps[opName] !== undefined) {
|
||||
fastOps[opName] = setUpAsyncStub(opName);
|
||||
} else {
|
||||
fastOps[opName] = ops[opName];
|
||||
}
|
||||
}
|
||||
return fastOps;
|
||||
}
|
||||
|
||||
const {
|
||||
op_close: close,
|
||||
op_try_close: tryClose,
|
||||
op_read: read,
|
||||
op_read_all: readAll,
|
||||
op_write: write,
|
||||
op_write_all: writeAll,
|
||||
op_read_sync: readSync,
|
||||
op_write_sync: writeSync,
|
||||
op_shutdown: shutdown,
|
||||
} = generateAsyncOpHandler(
|
||||
"op_close",
|
||||
"op_try_close",
|
||||
"op_read",
|
||||
"op_read_all",
|
||||
"op_write",
|
||||
"op_write_all",
|
||||
"op_read_sync",
|
||||
"op_write_sync",
|
||||
"op_shutdown",
|
||||
);
|
||||
|
||||
// Extra Deno.core.* exports
|
||||
const core = ObjectAssign(globalThis.Deno.core, {
|
||||
asyncStub,
|
||||
generateAsyncOpHandler,
|
||||
opAsync,
|
||||
opAsync2,
|
||||
resources,
|
||||
|
@ -460,15 +841,15 @@
|
|||
unrefOp,
|
||||
setReportExceptionCallback,
|
||||
setPromiseHooks,
|
||||
close: (rid) => ops.op_close(rid),
|
||||
tryClose: (rid) => ops.op_try_close(rid),
|
||||
read: opAsync.bind(null, "op_read"),
|
||||
readAll: opAsync.bind(null, "op_read_all"),
|
||||
write: opAsync.bind(null, "op_write"),
|
||||
writeAll: opAsync.bind(null, "op_write_all"),
|
||||
readSync: (rid, buffer) => ops.op_read_sync(rid, buffer),
|
||||
writeSync: (rid, buffer) => ops.op_write_sync(rid, buffer),
|
||||
shutdown: opAsync.bind(null, "op_shutdown"),
|
||||
close,
|
||||
tryClose,
|
||||
read,
|
||||
readAll,
|
||||
write,
|
||||
writeAll,
|
||||
readSync,
|
||||
writeSync,
|
||||
shutdown,
|
||||
print: (msg, isErr) => ops.op_print(msg, isErr),
|
||||
setMacrotaskCallback,
|
||||
setNextTickCallback,
|
||||
|
|
49
core/bindings.js
Normal file
49
core/bindings.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
if (!globalThis.Deno) {
|
||||
globalThis.Deno = {
|
||||
core: {
|
||||
ops: {},
|
||||
asyncOps: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Deno.__op__console = function (callConsole, console) {
|
||||
Deno.core.callConsole = callConsole;
|
||||
Deno.core.console = console;
|
||||
};
|
||||
|
||||
Deno.__op__registerOp = function (isAsync, op, opName) {
|
||||
const core = Deno.core;
|
||||
if (isAsync) {
|
||||
if (core.ops[opName] !== undefined) {
|
||||
return;
|
||||
}
|
||||
core.asyncOps[opName] = op;
|
||||
core.ops[opName] = function (...args) {
|
||||
if (this !== core.ops) {
|
||||
// deno-lint-ignore prefer-primordials
|
||||
throw new Error(
|
||||
"An async stub cannot be separated from Deno.core.ops. Use ???",
|
||||
);
|
||||
}
|
||||
return core.asyncStub(opName, args);
|
||||
};
|
||||
} else {
|
||||
core.ops[opName] = op;
|
||||
}
|
||||
};
|
||||
|
||||
Deno.__op__unregisterOp = function (isAsync, opName) {
|
||||
if (isAsync) {
|
||||
delete Deno.core.asyncOps[opName];
|
||||
}
|
||||
delete Deno.core.ops[opName];
|
||||
};
|
||||
|
||||
Deno.__op__cleanup = function () {
|
||||
delete Deno.__op__console;
|
||||
delete Deno.__op__registerOp;
|
||||
delete Deno.__op__unregisterOp;
|
||||
delete Deno.__op__cleanup;
|
||||
};
|
210
core/bindings.rs
210
core/bindings.rs
|
@ -1,9 +1,9 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use log::debug;
|
||||
use std::fmt::Write;
|
||||
use std::option::Option;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use log::debug;
|
||||
use v8::MapFnTo;
|
||||
|
||||
use crate::error::is_instance_of_error;
|
||||
|
@ -98,6 +98,23 @@ pub fn module_origin<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
fn get<'s, T>(
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
from: v8::Local<v8::Object>,
|
||||
key: &'static [u8],
|
||||
path: &'static str,
|
||||
) -> T
|
||||
where
|
||||
v8::Local<'s, v8::Value>: TryInto<T>,
|
||||
{
|
||||
let key = v8::String::new_external_onebyte_static(scope, key).unwrap();
|
||||
from
|
||||
.get(scope, key.into())
|
||||
.unwrap_or_else(|| panic!("{path} exists"))
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| panic!("unable to convert"))
|
||||
}
|
||||
|
||||
pub(crate) fn initialize_context<'s>(
|
||||
scope: &mut v8::HandleScope<'s, ()>,
|
||||
op_ctxs: &[OpCtx],
|
||||
|
@ -108,135 +125,92 @@ pub(crate) fn initialize_context<'s>(
|
|||
|
||||
let scope = &mut v8::ContextScope::new(scope, context);
|
||||
|
||||
let deno_str =
|
||||
v8::String::new_external_onebyte_static(scope, b"Deno").unwrap();
|
||||
let core_str =
|
||||
v8::String::new_external_onebyte_static(scope, b"core").unwrap();
|
||||
let ops_str = v8::String::new_external_onebyte_static(scope, b"ops").unwrap();
|
||||
let mut codegen = String::with_capacity(op_ctxs.len() * 200);
|
||||
codegen.push_str(include_str!("bindings.js"));
|
||||
_ = writeln!(
|
||||
codegen,
|
||||
"Deno.__op__ = function(opFns, callConsole, console) {{"
|
||||
);
|
||||
if !snapshot_options.loaded() {
|
||||
_ = writeln!(codegen, "Deno.__op__console(callConsole, console);");
|
||||
}
|
||||
for op_ctx in op_ctxs {
|
||||
if op_ctx.decl.enabled {
|
||||
// If we're loading from a snapshot, we can skip registration for most ops
|
||||
if matches!(snapshot_options, SnapshotOptions::Load)
|
||||
&& !op_ctx.decl.force_registration
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_ = writeln!(
|
||||
codegen,
|
||||
"Deno.__op__registerOp({}, opFns[{}], \"{}\");",
|
||||
op_ctx.decl.is_async, op_ctx.id, op_ctx.decl.name
|
||||
);
|
||||
} else {
|
||||
_ = writeln!(
|
||||
codegen,
|
||||
"Deno.__op__unregisterOp({}, \"{}\");",
|
||||
op_ctx.decl.is_async, op_ctx.decl.name
|
||||
);
|
||||
}
|
||||
}
|
||||
codegen.push_str("Deno.__op__cleanup();");
|
||||
_ = writeln!(codegen, "}}");
|
||||
|
||||
let ops_obj = if snapshot_options.loaded() {
|
||||
// Snapshot already registered `Deno.core.ops` but
|
||||
// extensions may provide ops that aren't part of the snapshot.
|
||||
// Grab the Deno.core.ops object & init it
|
||||
let deno_obj: v8::Local<v8::Object> = global
|
||||
.get(scope, deno_str.into())
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let core_obj: v8::Local<v8::Object> = deno_obj
|
||||
.get(scope, core_str.into())
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let ops_obj: v8::Local<v8::Object> = core_obj
|
||||
.get(scope, ops_str.into())
|
||||
.expect("Deno.core.ops to exist")
|
||||
.try_into()
|
||||
.unwrap();
|
||||
ops_obj
|
||||
let script = v8::String::new_from_one_byte(
|
||||
scope,
|
||||
codegen.as_bytes(),
|
||||
v8::NewStringType::Normal,
|
||||
)
|
||||
.unwrap();
|
||||
let script = v8::Script::compile(scope, script, None).unwrap();
|
||||
script.run(scope);
|
||||
|
||||
let deno = get(scope, global, b"Deno", "Deno");
|
||||
let op_fn: v8::Local<v8::Function> =
|
||||
get(scope, deno, b"__op__", "Deno.__op__");
|
||||
let recv = v8::undefined(scope);
|
||||
let op_fns = v8::Array::new(scope, op_ctxs.len() as i32);
|
||||
for op_ctx in op_ctxs {
|
||||
let op_fn = op_ctx_function(scope, op_ctx);
|
||||
op_fns.set_index(scope, op_ctx.id as u32, op_fn.into());
|
||||
}
|
||||
if snapshot_options.loaded() {
|
||||
op_fn.call(scope, recv.into(), &[op_fns.into()]);
|
||||
} else {
|
||||
// globalThis.Deno = { core: { } };
|
||||
let deno_obj = v8::Object::new(scope);
|
||||
global.set(scope, deno_str.into(), deno_obj.into());
|
||||
|
||||
let core_obj = v8::Object::new(scope);
|
||||
deno_obj.set(scope, core_str.into(), core_obj.into());
|
||||
|
||||
// Bind functions to Deno.core.*
|
||||
set_func(scope, core_obj, "callConsole", call_console);
|
||||
let call_console_fn = v8::Function::new(scope, call_console).unwrap();
|
||||
|
||||
// Bind v8 console object to Deno.core.console
|
||||
let extra_binding_obj = context.get_extras_binding_object(scope);
|
||||
let console_str =
|
||||
v8::String::new_external_onebyte_static(scope, b"console").unwrap();
|
||||
let console_obj = extra_binding_obj.get(scope, console_str.into()).unwrap();
|
||||
core_obj.set(scope, console_str.into(), console_obj);
|
||||
let console_obj: v8::Local<v8::Object> = get(
|
||||
scope,
|
||||
extra_binding_obj,
|
||||
b"console",
|
||||
"ExtrasBindingObject.console",
|
||||
);
|
||||
|
||||
// Bind functions to Deno.core.ops.*
|
||||
let ops_obj = v8::Object::new(scope);
|
||||
core_obj.set(scope, ops_str.into(), ops_obj.into());
|
||||
ops_obj
|
||||
};
|
||||
|
||||
if matches!(snapshot_options, SnapshotOptions::Load) {
|
||||
// Only register ops that have `force_registration` flag set to true,
|
||||
// the remaining ones should already be in the snapshot. Ignore ops that
|
||||
// are disabled.
|
||||
for op_ctx in op_ctxs {
|
||||
if op_ctx.decl.enabled {
|
||||
if op_ctx.decl.force_registration {
|
||||
add_op_to_deno_core_ops(scope, ops_obj, op_ctx);
|
||||
}
|
||||
} else {
|
||||
delete_op_from_deno_core_ops(scope, ops_obj, op_ctx)
|
||||
}
|
||||
}
|
||||
} else if matches!(snapshot_options, SnapshotOptions::CreateFromExisting) {
|
||||
// Register all enabled ops, probing for which ones are already registered.
|
||||
for op_ctx in op_ctxs {
|
||||
let key = v8::String::new_external_onebyte_static(
|
||||
scope,
|
||||
op_ctx.decl.name.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if op_ctx.decl.enabled {
|
||||
if ops_obj.get(scope, key.into()).is_some() {
|
||||
continue;
|
||||
}
|
||||
add_op_to_deno_core_ops(scope, ops_obj, op_ctx);
|
||||
} else {
|
||||
delete_op_from_deno_core_ops(scope, ops_obj, op_ctx)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// In other cases register all ops enabled unconditionally.
|
||||
for op_ctx in op_ctxs {
|
||||
if op_ctx.decl.enabled {
|
||||
add_op_to_deno_core_ops(scope, ops_obj, op_ctx);
|
||||
}
|
||||
}
|
||||
op_fn.call(
|
||||
scope,
|
||||
recv.into(),
|
||||
&[op_fns.into(), call_console_fn.into(), console_obj.into()],
|
||||
);
|
||||
}
|
||||
|
||||
context
|
||||
}
|
||||
|
||||
fn set_func(
|
||||
scope: &mut v8::HandleScope<'_>,
|
||||
obj: v8::Local<v8::Object>,
|
||||
name: &'static str,
|
||||
callback: impl v8::MapFnTo<v8::FunctionCallback>,
|
||||
) {
|
||||
let key =
|
||||
v8::String::new_external_onebyte_static(scope, name.as_bytes()).unwrap();
|
||||
let val = v8::Function::new(scope, callback).unwrap();
|
||||
val.set_name(key);
|
||||
obj.set(scope, key.into(), val.into());
|
||||
}
|
||||
|
||||
fn delete_op_from_deno_core_ops(
|
||||
scope: &mut v8::HandleScope<'_>,
|
||||
obj: v8::Local<v8::Object>,
|
||||
fn op_ctx_function<'s>(
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
op_ctx: &OpCtx,
|
||||
) {
|
||||
let key =
|
||||
v8::String::new_external_onebyte_static(scope, op_ctx.decl.name.as_bytes())
|
||||
.unwrap();
|
||||
obj.delete(scope, key.into());
|
||||
}
|
||||
|
||||
fn add_op_to_deno_core_ops(
|
||||
scope: &mut v8::HandleScope<'_>,
|
||||
obj: v8::Local<v8::Object>,
|
||||
op_ctx: &OpCtx,
|
||||
) {
|
||||
) -> v8::Local<'s, v8::Function> {
|
||||
let op_ctx_ptr = op_ctx as *const OpCtx as *const c_void;
|
||||
let key =
|
||||
v8::String::new_external_onebyte_static(scope, op_ctx.decl.name.as_bytes())
|
||||
.unwrap();
|
||||
let external = v8::External::new(scope, op_ctx_ptr as *mut c_void);
|
||||
let builder = v8::FunctionTemplate::builder_raw(op_ctx.decl.v8_fn_ptr)
|
||||
.data(external.into());
|
||||
let builder: v8::FunctionBuilder<v8::FunctionTemplate> =
|
||||
v8::FunctionTemplate::builder_raw(op_ctx.decl.v8_fn_ptr)
|
||||
.data(external.into())
|
||||
.length(op_ctx.decl.arg_count as i32);
|
||||
|
||||
let templ = if let Some(fast_function) = &op_ctx.decl.fast_fn {
|
||||
builder.build_fast(
|
||||
|
@ -249,9 +223,7 @@ fn add_op_to_deno_core_ops(
|
|||
} else {
|
||||
builder.build(scope)
|
||||
};
|
||||
let val = templ.get_function(scope).unwrap();
|
||||
val.set_name(key);
|
||||
obj.set(scope, key.into(), val.into());
|
||||
templ.get_function(scope).unwrap()
|
||||
}
|
||||
|
||||
pub extern "C" fn wasm_async_resolve_promise_callback(
|
||||
|
|
|
@ -73,6 +73,7 @@ pub struct OpDecl {
|
|||
pub is_unstable: bool,
|
||||
pub is_v8: bool,
|
||||
pub force_registration: bool,
|
||||
pub arg_count: u8,
|
||||
pub fast_fn: Option<FastFunction>,
|
||||
}
|
||||
|
||||
|
|
8
core/lib.deno_core.d.ts
vendored
8
core/lib.deno_core.d.ts
vendored
|
@ -23,10 +23,16 @@ declare namespace Deno {
|
|||
|
||||
/**
|
||||
* List of all registered ops, in the form of a map that maps op
|
||||
* name to internal numerical op id.
|
||||
* name to function.
|
||||
*/
|
||||
const ops: Record<string, (...args: unknown[]) => any>;
|
||||
|
||||
/**
|
||||
* List of all registered async ops, in the form of a map that maps op
|
||||
* name to function.
|
||||
*/
|
||||
const asyncOps: Record<string, (...args: unknown[]) => any>;
|
||||
|
||||
/**
|
||||
* Retrieve a list of all open resources, in the form of a map that maps
|
||||
* resource id to the resource name.
|
||||
|
|
|
@ -27,9 +27,12 @@ crate::extension!(
|
|||
op_wasm_streaming_feed,
|
||||
op_wasm_streaming_set_url,
|
||||
op_void_sync,
|
||||
op_error_async,
|
||||
op_error_async_deferred,
|
||||
op_void_async,
|
||||
op_void_async_deferred,
|
||||
op_add,
|
||||
op_add_async,
|
||||
// TODO(@AaronO): track IO metrics for builtin streams
|
||||
op_read,
|
||||
op_read_all,
|
||||
|
@ -96,12 +99,27 @@ fn op_add(a: i32, b: i32) -> i32 {
|
|||
a + b
|
||||
}
|
||||
|
||||
#[op]
|
||||
pub async fn op_add_async(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
|
||||
#[op(fast)]
|
||||
pub fn op_void_sync() {}
|
||||
|
||||
#[op]
|
||||
pub async fn op_void_async() {}
|
||||
|
||||
#[op]
|
||||
pub async fn op_error_async() -> Result<(), Error> {
|
||||
Err(Error::msg("error"))
|
||||
}
|
||||
|
||||
#[op(deferred)]
|
||||
pub async fn op_error_async_deferred() -> Result<(), Error> {
|
||||
Err(Error::msg("error"))
|
||||
}
|
||||
|
||||
#[op(deferred)]
|
||||
pub async fn op_void_async_deferred() {}
|
||||
|
||||
|
|
|
@ -3737,21 +3737,6 @@ assertEquals(1, notify_return_value);
|
|||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_core_js_stack_frame() {
|
||||
let mut runtime = JsRuntime::new(RuntimeOptions::default());
|
||||
// Call non-existent op so we get error from `core.js`
|
||||
let error = runtime
|
||||
.execute_script_static(
|
||||
"core_js_stack_frame.js",
|
||||
"Deno.core.opAsync('non_existent');",
|
||||
)
|
||||
.unwrap_err();
|
||||
let error_string = error.to_string();
|
||||
// Test that the script specifier is a URL: `ext:<repo-relative path>`.
|
||||
assert!(error_string.contains("ext:core/01_core.js"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_v8_platform() {
|
||||
let options = RuntimeOptions {
|
||||
|
@ -4721,21 +4706,6 @@ Deno.core.opAsync("op_async_serialize_object_with_numbers_as_keys", {
|
|||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_existent_async_op_error() {
|
||||
// Verify that "resizable ArrayBuffer" is disabled
|
||||
let mut runtime = JsRuntime::new(Default::default());
|
||||
let err = runtime
|
||||
.execute_script_static(
|
||||
"test_rab.js",
|
||||
r#"Deno.core.opAsync("this_op_doesnt_exist");"#,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(err
|
||||
.to_string()
|
||||
.contains("this_op_doesnt_exist is not a registered op"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn cant_load_internal_module_when_snapshot_is_loaded_and_not_snapshotting(
|
||||
) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
// deno-lint-ignore-file camelcase
|
||||
const core = globalThis.Deno.core;
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const internals = globalThis.__bootstrap.internals;
|
||||
|
@ -46,6 +47,39 @@ const {
|
|||
Uint8Array,
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
op_http_wait,
|
||||
op_upgrade,
|
||||
op_get_request_headers,
|
||||
op_get_request_method_and_url,
|
||||
op_read_request_body,
|
||||
op_serve_http,
|
||||
op_set_promise_complete,
|
||||
op_set_response_body_bytes,
|
||||
op_set_response_body_resource,
|
||||
op_set_response_body_stream,
|
||||
op_set_response_body_text,
|
||||
op_set_response_header,
|
||||
op_set_response_headers,
|
||||
op_upgrade_raw,
|
||||
op_ws_server_create,
|
||||
} = Deno.core.generateAsyncOpHandler(
|
||||
"op_http_wait",
|
||||
"op_upgrade",
|
||||
"op_get_request_headers",
|
||||
"op_get_request_method_and_url",
|
||||
"op_read_request_body",
|
||||
"op_serve_http",
|
||||
"op_set_promise_complete",
|
||||
"op_set_response_body_bytes",
|
||||
"op_set_response_body_resource",
|
||||
"op_set_response_body_stream",
|
||||
"op_set_response_body_text",
|
||||
"op_set_response_header",
|
||||
"op_set_response_headers",
|
||||
"op_upgrade_raw",
|
||||
"op_ws_server_create",
|
||||
);
|
||||
const _upgraded = Symbol("_upgraded");
|
||||
|
||||
function internalServerError() {
|
||||
|
@ -143,7 +177,7 @@ class InnerRequest {
|
|||
|
||||
this.#upgraded = () => {};
|
||||
|
||||
const upgradeRid = core.ops.op_upgrade_raw(slabId);
|
||||
const upgradeRid = op_upgrade_raw(slabId);
|
||||
|
||||
const conn = new TcpConn(
|
||||
upgradeRid,
|
||||
|
@ -174,12 +208,11 @@ class InnerRequest {
|
|||
(async () => {
|
||||
try {
|
||||
// Returns the connection and extra bytes, which we can pass directly to op_ws_server_create
|
||||
const upgrade = await core.opAsync2(
|
||||
"op_upgrade",
|
||||
const upgrade = await op_upgrade(
|
||||
slabId,
|
||||
response.headerList,
|
||||
);
|
||||
const wsRid = core.ops.op_ws_server_create(upgrade[0], upgrade[1]);
|
||||
const wsRid = op_ws_server_create(upgrade[0], upgrade[1]);
|
||||
|
||||
// We have to wait for the go-ahead signal
|
||||
await goAhead;
|
||||
|
@ -214,7 +247,7 @@ class InnerRequest {
|
|||
}
|
||||
// TODO(mmastrac): This is quite slow as we're serializing a large number of values. We may want to consider
|
||||
// splitting this up into multiple ops.
|
||||
this.#methodAndUri = core.ops.op_get_request_method_and_url(this.#slabId);
|
||||
this.#methodAndUri = op_get_request_method_and_url(this.#slabId);
|
||||
}
|
||||
|
||||
const path = this.#methodAndUri[2];
|
||||
|
@ -249,7 +282,7 @@ class InnerRequest {
|
|||
if (this.#slabId === undefined) {
|
||||
throw new TypeError("request closed");
|
||||
}
|
||||
this.#methodAndUri = core.ops.op_get_request_method_and_url(this.#slabId);
|
||||
this.#methodAndUri = op_get_request_method_and_url(this.#slabId);
|
||||
}
|
||||
return {
|
||||
transport: "tcp",
|
||||
|
@ -263,7 +296,7 @@ class InnerRequest {
|
|||
if (this.#slabId === undefined) {
|
||||
throw new TypeError("request closed");
|
||||
}
|
||||
this.#methodAndUri = core.ops.op_get_request_method_and_url(this.#slabId);
|
||||
this.#methodAndUri = op_get_request_method_and_url(this.#slabId);
|
||||
}
|
||||
return this.#methodAndUri[0];
|
||||
}
|
||||
|
@ -281,7 +314,7 @@ class InnerRequest {
|
|||
this.#body = null;
|
||||
return null;
|
||||
}
|
||||
this.#streamRid = core.ops.op_read_request_body(this.#slabId);
|
||||
this.#streamRid = op_read_request_body(this.#slabId);
|
||||
this.#body = new InnerBody(readableStreamForRid(this.#streamRid, false));
|
||||
return this.#body;
|
||||
}
|
||||
|
@ -290,7 +323,7 @@ class InnerRequest {
|
|||
if (this.#slabId === undefined) {
|
||||
throw new TypeError("request closed");
|
||||
}
|
||||
return core.ops.op_get_request_headers(this.#slabId);
|
||||
return op_get_request_headers(this.#slabId);
|
||||
}
|
||||
|
||||
get slabId() {
|
||||
|
@ -331,12 +364,12 @@ function fastSyncResponseOrStream(req, respBody) {
|
|||
const body = stream.body;
|
||||
|
||||
if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, body)) {
|
||||
core.ops.op_set_response_body_bytes(req, body);
|
||||
op_set_response_body_bytes(req, body);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof body === "string") {
|
||||
core.ops.op_set_response_body_text(req, body);
|
||||
op_set_response_body_text(req, body);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -346,7 +379,7 @@ function fastSyncResponseOrStream(req, respBody) {
|
|||
}
|
||||
const resourceBacking = getReadableStreamResourceBacking(stream);
|
||||
if (resourceBacking) {
|
||||
core.ops.op_set_response_body_resource(
|
||||
op_set_response_body_resource(
|
||||
req,
|
||||
resourceBacking.rid,
|
||||
resourceBacking.autoClose,
|
||||
|
@ -382,9 +415,9 @@ async function asyncResponse(responseBodies, req, status, stream) {
|
|||
// and we race it.
|
||||
let timeoutPromise;
|
||||
timeout = setTimeout(() => {
|
||||
responseRid = core.ops.op_set_response_body_stream(req);
|
||||
responseRid = op_set_response_body_stream(req);
|
||||
SetPrototypeAdd(responseBodies, responseRid);
|
||||
core.ops.op_set_promise_complete(req, status);
|
||||
op_set_promise_complete(req, status);
|
||||
timeoutPromise = core.writeAll(responseRid, value1);
|
||||
}, 250);
|
||||
const { value: value2, done: done2 } = await reader.read();
|
||||
|
@ -409,13 +442,13 @@ async function asyncResponse(responseBodies, req, status, stream) {
|
|||
// Reader will be closed by finally block
|
||||
// No response stream
|
||||
closed = true;
|
||||
core.ops.op_set_response_body_bytes(req, value1);
|
||||
op_set_response_body_bytes(req, value1);
|
||||
return;
|
||||
}
|
||||
|
||||
responseRid = core.ops.op_set_response_body_stream(req);
|
||||
responseRid = op_set_response_body_stream(req);
|
||||
SetPrototypeAdd(responseBodies, responseRid);
|
||||
core.ops.op_set_promise_complete(req, status);
|
||||
op_set_promise_complete(req, status);
|
||||
// Write our first packet
|
||||
await core.writeAll(responseRid, value1);
|
||||
}
|
||||
|
@ -447,7 +480,7 @@ async function asyncResponse(responseBodies, req, status, stream) {
|
|||
core.tryClose(responseRid);
|
||||
SetPrototypeDelete(responseBodies, responseRid);
|
||||
} else {
|
||||
core.ops.op_set_promise_complete(req, status);
|
||||
op_set_promise_complete(req, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -511,9 +544,9 @@ function mapToCallback(responseBodies, context, signal, callback, onError) {
|
|||
const headers = inner.headerList;
|
||||
if (headers && headers.length > 0) {
|
||||
if (headers.length == 1) {
|
||||
core.ops.op_set_response_header(req, headers[0][0], headers[0][1]);
|
||||
op_set_response_header(req, headers[0][0], headers[0][1]);
|
||||
} else {
|
||||
core.ops.op_set_response_headers(req, headers);
|
||||
op_set_response_headers(req, headers);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -523,7 +556,7 @@ function mapToCallback(responseBodies, context, signal, callback, onError) {
|
|||
// Handle the stream asynchronously
|
||||
await asyncResponse(responseBodies, req, status, stream);
|
||||
} else {
|
||||
core.ops.op_set_promise_complete(req, status);
|
||||
op_set_promise_complete(req, status);
|
||||
}
|
||||
|
||||
innerRequest?.close();
|
||||
|
@ -591,13 +624,13 @@ async function serve(arg1, arg2) {
|
|||
listenOpts.alpnProtocols = ["h2", "http/1.1"];
|
||||
const listener = Deno.listenTls(listenOpts);
|
||||
listenOpts.port = listener.addr.port;
|
||||
context.initialize(core.ops.op_serve_http(
|
||||
context.initialize(op_serve_http(
|
||||
listener.rid,
|
||||
));
|
||||
} else {
|
||||
const listener = Deno.listen(listenOpts);
|
||||
listenOpts.port = listener.addr.port;
|
||||
context.initialize(core.ops.op_serve_http(
|
||||
context.initialize(op_serve_http(
|
||||
listener.rid,
|
||||
));
|
||||
}
|
||||
|
@ -624,7 +657,7 @@ async function serve(arg1, arg2) {
|
|||
const rid = context.serverRid;
|
||||
let req;
|
||||
try {
|
||||
req = await core.opAsync2("op_http_wait", rid);
|
||||
req = await op_http_wait(rid);
|
||||
} catch (error) {
|
||||
if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) {
|
||||
break;
|
||||
|
|
24
ops/lib.rs
24
ops/lib.rs
|
@ -144,6 +144,8 @@ impl Op {
|
|||
is_unstable: #is_unstable,
|
||||
is_v8: #is_v8,
|
||||
force_registration: false,
|
||||
// TODO(mmastrac)
|
||||
arg_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,8 +160,8 @@ impl Op {
|
|||
|
||||
let has_fallible_fast_call = active && optimizer.returns_result;
|
||||
|
||||
let v8_body = if is_async {
|
||||
let deferred = attrs.deferred;
|
||||
let (v8_body, arg_count) = if is_async {
|
||||
let deferred: bool = attrs.deferred;
|
||||
codegen_v8_async(
|
||||
&core,
|
||||
&item,
|
||||
|
@ -205,6 +207,7 @@ impl Op {
|
|||
is_unstable: #is_unstable,
|
||||
is_v8: #is_v8,
|
||||
force_registration: false,
|
||||
arg_count: #arg_count as u8,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +244,7 @@ fn codegen_v8_async(
|
|||
margs: Attributes,
|
||||
asyncness: bool,
|
||||
deferred: bool,
|
||||
) -> TokenStream2 {
|
||||
) -> (TokenStream2, usize) {
|
||||
let Attributes { is_v8, .. } = margs;
|
||||
let special_args = f
|
||||
.sig
|
||||
|
@ -309,7 +312,7 @@ fn codegen_v8_async(
|
|||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
let token_stream = quote! {
|
||||
use #core::futures::FutureExt;
|
||||
// SAFETY: #core guarantees args.data() is a v8 External pointing to an OpCtx for the isolates lifetime
|
||||
let ctx = unsafe {
|
||||
|
@ -336,7 +339,10 @@ fn codegen_v8_async(
|
|||
if let Some(response) = maybe_response {
|
||||
rv.set(response);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// +1 arg for the promise ID
|
||||
(token_stream, 1 + f.sig.inputs.len() - rust_i0)
|
||||
}
|
||||
|
||||
fn scope_arg(arg: &FnArg) -> Option<TokenStream2> {
|
||||
|
@ -373,7 +379,7 @@ fn codegen_v8_sync(
|
|||
f: &syn::ItemFn,
|
||||
margs: Attributes,
|
||||
has_fallible_fast_call: bool,
|
||||
) -> TokenStream2 {
|
||||
) -> (TokenStream2, usize) {
|
||||
let Attributes { is_v8, .. } = margs;
|
||||
let special_args = f
|
||||
.sig
|
||||
|
@ -404,7 +410,7 @@ fn codegen_v8_sync(
|
|||
quote! {}
|
||||
};
|
||||
|
||||
quote! {
|
||||
let token_stream = quote! {
|
||||
// SAFETY: #core guarantees args.data() is a v8 External pointing to an OpCtx for the isolates lifetime
|
||||
let ctx = unsafe {
|
||||
&*(#core::v8::Local::<#core::v8::External>::cast(args.data()).value()
|
||||
|
@ -421,7 +427,9 @@ fn codegen_v8_sync(
|
|||
op_state.tracker.track_sync(ctx.id);
|
||||
|
||||
#ret
|
||||
}
|
||||
};
|
||||
|
||||
(token_stream, f.sig.inputs.len() - rust_i0)
|
||||
}
|
||||
|
||||
/// (full declarations, idents, v8 argument count)
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_void_async {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_async_result {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_fallback {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_cow_str {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_f64_buf {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ impl op_sync_serialize_object_with_numbers_as_keys {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ impl send_stdin {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ impl send_stdin {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ impl op_blob_revoke_object_url {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_ffi_ptr_value {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ impl op_print {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_set_exit_code {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl foo {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -47,6 +47,7 @@ impl op_foo {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 0usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl foo {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_listen {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 0usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -47,6 +47,7 @@ impl op_now {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_add_4 {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 4usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ impl op_try_close {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_string_length {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ impl op_read_sync {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -52,6 +52,7 @@ impl op_ffi_ptr_of {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_is_proxy {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_string_length {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ impl op_string_length {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -31,6 +31,7 @@ impl op_bench_now {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 0usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_import_spki_x25519 {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_unit_result {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 0usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_set_nodelay {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 2usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_unit {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 0usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
|
@ -41,6 +41,7 @@ impl op_wasm {
|
|||
is_unstable: false,
|
||||
is_v8: false,
|
||||
force_registration: false,
|
||||
arg_count: 1usize as u8,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
|
Loading…
Reference in a new issue