mirror of
https://github.com/denoland/deno.git
synced 2025-08-30 23:38:03 +00:00
feat(ext/node): embed std/node into the snapshot (#17724)
This commit moves "deno_std/node" in "ext/node" crate. The code is transpiled and snapshotted during the build process. During the first pass a minimal amount of work was done to create the snapshot, a lot of code in "ext/node" depends on presence of "Deno" global. This code will be gradually fixed in the follow up PRs to migrate it to import relevant APIs from "internal:" modules. Currently the code from snapshot is not used in any way, and all Node/npm compatibility still uses code from "https://deno.land/std/node" (or from the location specified by "DENO_NODE_COMPAT_URL"). This will also be handled in a follow up PRs. --------- Co-authored-by: crowlkats <crowlkats@toaxl.com> Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com> Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
This commit is contained in:
parent
1d00bbe47e
commit
d47147fb6a
332 changed files with 98173 additions and 8 deletions
420
ext/node/polyfills/internal/async_hooks.ts
Normal file
420
ext/node/polyfills/internal/async_hooks.ts
Normal file
|
@ -0,0 +1,420 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
|
||||
// deno-lint-ignore camelcase
|
||||
import * as async_wrap from "internal:deno_node/polyfills/internal_binding/async_wrap.ts";
|
||||
import { ERR_ASYNC_CALLBACK } from "internal:deno_node/polyfills/internal/errors.ts";
|
||||
export {
|
||||
asyncIdSymbol,
|
||||
ownerSymbol,
|
||||
} from "internal:deno_node/polyfills/internal_binding/symbols.ts";
|
||||
|
||||
interface ActiveHooks {
|
||||
array: AsyncHook[];
|
||||
// deno-lint-ignore camelcase
|
||||
call_depth: number;
|
||||
// deno-lint-ignore camelcase
|
||||
tmp_array: AsyncHook[] | null;
|
||||
// deno-lint-ignore camelcase
|
||||
tmp_fields: number[] | null;
|
||||
}
|
||||
|
||||
// Properties in active_hooks are used to keep track of the set of hooks being
|
||||
// executed in case another hook is enabled/disabled. The new set of hooks is
|
||||
// then restored once the active set of hooks is finished executing.
|
||||
// deno-lint-ignore camelcase
|
||||
const active_hooks: ActiveHooks = {
|
||||
// Array of all AsyncHooks that will be iterated whenever an async event
|
||||
// fires. Using var instead of (preferably const) in order to assign
|
||||
// active_hooks.tmp_array if a hook is enabled/disabled during hook
|
||||
// execution.
|
||||
array: [],
|
||||
// Use a counter to track nested calls of async hook callbacks and make sure
|
||||
// the active_hooks.array isn't altered mid execution.
|
||||
// deno-lint-ignore camelcase
|
||||
call_depth: 0,
|
||||
// Use to temporarily store and updated active_hooks.array if the user
|
||||
// enables or disables a hook while hooks are being processed. If a hook is
|
||||
// enabled() or disabled() during hook execution then the current set of
|
||||
// active hooks is duplicated and set equal to active_hooks.tmp_array. Any
|
||||
// subsequent changes are on the duplicated array. When all hooks have
|
||||
// completed executing active_hooks.tmp_array is assigned to
|
||||
// active_hooks.array.
|
||||
// deno-lint-ignore camelcase
|
||||
tmp_array: null,
|
||||
// Keep track of the field counts held in active_hooks.tmp_array. Because the
|
||||
// async_hook_fields can't be reassigned, store each uint32 in an array that
|
||||
// is written back to async_hook_fields when active_hooks.array is restored.
|
||||
// deno-lint-ignore camelcase
|
||||
tmp_fields: null,
|
||||
};
|
||||
|
||||
export const registerDestroyHook = async_wrap.registerDestroyHook;
|
||||
const {
|
||||
// deno-lint-ignore camelcase
|
||||
async_hook_fields,
|
||||
// deno-lint-ignore camelcase
|
||||
asyncIdFields: async_id_fields,
|
||||
newAsyncId,
|
||||
constants,
|
||||
} = async_wrap;
|
||||
export { newAsyncId };
|
||||
const {
|
||||
kInit,
|
||||
kBefore,
|
||||
kAfter,
|
||||
kDestroy,
|
||||
kPromiseResolve,
|
||||
kTotals,
|
||||
kCheck,
|
||||
kDefaultTriggerAsyncId,
|
||||
kStackLength,
|
||||
} = constants;
|
||||
|
||||
// deno-lint-ignore camelcase
|
||||
const resource_symbol = Symbol("resource");
|
||||
// deno-lint-ignore camelcase
|
||||
export const async_id_symbol = Symbol("trigger_async_id");
|
||||
// deno-lint-ignore camelcase
|
||||
export const trigger_async_id_symbol = Symbol("trigger_async_id");
|
||||
// deno-lint-ignore camelcase
|
||||
export const init_symbol = Symbol("init");
|
||||
// deno-lint-ignore camelcase
|
||||
export const before_symbol = Symbol("before");
|
||||
// deno-lint-ignore camelcase
|
||||
export const after_symbol = Symbol("after");
|
||||
// deno-lint-ignore camelcase
|
||||
export const destroy_symbol = Symbol("destroy");
|
||||
// deno-lint-ignore camelcase
|
||||
export const promise_resolve_symbol = Symbol("promiseResolve");
|
||||
|
||||
export const symbols = {
|
||||
// deno-lint-ignore camelcase
|
||||
async_id_symbol,
|
||||
// deno-lint-ignore camelcase
|
||||
trigger_async_id_symbol,
|
||||
// deno-lint-ignore camelcase
|
||||
init_symbol,
|
||||
// deno-lint-ignore camelcase
|
||||
before_symbol,
|
||||
// deno-lint-ignore camelcase
|
||||
after_symbol,
|
||||
// deno-lint-ignore camelcase
|
||||
destroy_symbol,
|
||||
// deno-lint-ignore camelcase
|
||||
promise_resolve_symbol,
|
||||
};
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
function lookupPublicResource(resource: any) {
|
||||
if (typeof resource !== "object" || resource === null) return resource;
|
||||
// TODO(addaleax): Merge this with owner_symbol and use it across all
|
||||
// AsyncWrap instances.
|
||||
const publicResource = resource[resource_symbol];
|
||||
if (publicResource !== undefined) {
|
||||
return publicResource;
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
// Used by C++ to call all init() callbacks. Because some state can be setup
|
||||
// from C++ there's no need to perform all the same operations as in
|
||||
// emitInitScript.
|
||||
function emitInitNative(
|
||||
asyncId: number,
|
||||
// deno-lint-ignore no-explicit-any
|
||||
type: any,
|
||||
triggerAsyncId: number,
|
||||
// deno-lint-ignore no-explicit-any
|
||||
resource: any,
|
||||
) {
|
||||
active_hooks.call_depth += 1;
|
||||
resource = lookupPublicResource(resource);
|
||||
// Use a single try/catch for all hooks to avoid setting up one per iteration.
|
||||
try {
|
||||
for (let i = 0; i < active_hooks.array.length; i++) {
|
||||
if (typeof active_hooks.array[i][init_symbol] === "function") {
|
||||
active_hooks.array[i][init_symbol](
|
||||
asyncId,
|
||||
type,
|
||||
triggerAsyncId,
|
||||
resource,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw e;
|
||||
} finally {
|
||||
active_hooks.call_depth -= 1;
|
||||
}
|
||||
|
||||
// Hooks can only be restored if there have been no recursive hook calls.
|
||||
// Also the active hooks do not need to be restored if enable()/disable()
|
||||
// weren't called during hook execution, in which case active_hooks.tmp_array
|
||||
// will be null.
|
||||
if (active_hooks.call_depth === 0 && active_hooks.tmp_array !== null) {
|
||||
restoreActiveHooks();
|
||||
}
|
||||
}
|
||||
|
||||
function getHookArrays(): [AsyncHook[], number[] | Uint32Array] {
|
||||
if (active_hooks.call_depth === 0) {
|
||||
return [active_hooks.array, async_hook_fields];
|
||||
}
|
||||
// If this hook is being enabled while in the middle of processing the array
|
||||
// of currently active hooks then duplicate the current set of active hooks
|
||||
// and store this there. This shouldn't fire until the next time hooks are
|
||||
// processed.
|
||||
if (active_hooks.tmp_array === null) {
|
||||
storeActiveHooks();
|
||||
}
|
||||
return [active_hooks.tmp_array!, active_hooks.tmp_fields!];
|
||||
}
|
||||
|
||||
function storeActiveHooks() {
|
||||
active_hooks.tmp_array = active_hooks.array.slice();
|
||||
// Don't want to make the assumption that kInit to kDestroy are indexes 0 to
|
||||
// 4. So do this the long way.
|
||||
active_hooks.tmp_fields = [];
|
||||
copyHooks(active_hooks.tmp_fields, async_hook_fields);
|
||||
}
|
||||
|
||||
function copyHooks(
|
||||
destination: number[] | Uint32Array,
|
||||
source: number[] | Uint32Array,
|
||||
) {
|
||||
destination[kInit] = source[kInit];
|
||||
destination[kBefore] = source[kBefore];
|
||||
destination[kAfter] = source[kAfter];
|
||||
destination[kDestroy] = source[kDestroy];
|
||||
destination[kPromiseResolve] = source[kPromiseResolve];
|
||||
}
|
||||
|
||||
// Then restore the correct hooks array in case any hooks were added/removed
|
||||
// during hook callback execution.
|
||||
function restoreActiveHooks() {
|
||||
active_hooks.array = active_hooks.tmp_array!;
|
||||
copyHooks(async_hook_fields, active_hooks.tmp_fields!);
|
||||
|
||||
active_hooks.tmp_array = null;
|
||||
active_hooks.tmp_fields = null;
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-unused-vars
|
||||
let wantPromiseHook = false;
|
||||
function enableHooks() {
|
||||
async_hook_fields[kCheck] += 1;
|
||||
|
||||
// TODO(kt3k): Uncomment this
|
||||
// setCallbackTrampoline(callbackTrampoline);
|
||||
}
|
||||
|
||||
function disableHooks() {
|
||||
async_hook_fields[kCheck] -= 1;
|
||||
|
||||
wantPromiseHook = false;
|
||||
|
||||
// TODO(kt3k): Uncomment the below
|
||||
// setCallbackTrampoline();
|
||||
|
||||
// Delay the call to `disablePromiseHook()` because we might currently be
|
||||
// between the `before` and `after` calls of a Promise.
|
||||
// TODO(kt3k): Uncomment the below
|
||||
// enqueueMicrotask(disablePromiseHookIfNecessary);
|
||||
}
|
||||
|
||||
// Return the triggerAsyncId meant for the constructor calling it. It's up to
|
||||
// the user to safeguard this call and make sure it's zero'd out when the
|
||||
// constructor is complete.
|
||||
export function getDefaultTriggerAsyncId() {
|
||||
const defaultTriggerAsyncId =
|
||||
async_id_fields[async_wrap.UidFields.kDefaultTriggerAsyncId];
|
||||
// If defaultTriggerAsyncId isn't set, use the executionAsyncId
|
||||
if (defaultTriggerAsyncId < 0) {
|
||||
return async_id_fields[async_wrap.UidFields.kExecutionAsyncId];
|
||||
}
|
||||
return defaultTriggerAsyncId;
|
||||
}
|
||||
|
||||
export function defaultTriggerAsyncIdScope(
|
||||
triggerAsyncId: number | undefined,
|
||||
// deno-lint-ignore no-explicit-any
|
||||
block: (...arg: any[]) => void,
|
||||
...args: unknown[]
|
||||
) {
|
||||
if (triggerAsyncId === undefined) {
|
||||
return block.apply(null, args);
|
||||
}
|
||||
// CHECK(NumberIsSafeInteger(triggerAsyncId))
|
||||
// CHECK(triggerAsyncId > 0)
|
||||
const oldDefaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId];
|
||||
async_id_fields[kDefaultTriggerAsyncId] = triggerAsyncId;
|
||||
|
||||
try {
|
||||
return block.apply(null, args);
|
||||
} finally {
|
||||
async_id_fields[kDefaultTriggerAsyncId] = oldDefaultTriggerAsyncId;
|
||||
}
|
||||
}
|
||||
|
||||
function hasHooks(key: number) {
|
||||
return async_hook_fields[key] > 0;
|
||||
}
|
||||
|
||||
export function enabledHooksExist() {
|
||||
return hasHooks(kCheck);
|
||||
}
|
||||
|
||||
export function initHooksExist() {
|
||||
return hasHooks(kInit);
|
||||
}
|
||||
|
||||
export function afterHooksExist() {
|
||||
return hasHooks(kAfter);
|
||||
}
|
||||
|
||||
export function destroyHooksExist() {
|
||||
return hasHooks(kDestroy);
|
||||
}
|
||||
|
||||
export function promiseResolveHooksExist() {
|
||||
return hasHooks(kPromiseResolve);
|
||||
}
|
||||
|
||||
function emitInitScript(
|
||||
asyncId: number,
|
||||
// deno-lint-ignore no-explicit-any
|
||||
type: any,
|
||||
triggerAsyncId: number,
|
||||
// deno-lint-ignore no-explicit-any
|
||||
resource: any,
|
||||
) {
|
||||
// Short circuit all checks for the common case. Which is that no hooks have
|
||||
// been set. Do this to remove performance impact for embedders (and core).
|
||||
if (!hasHooks(kInit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (triggerAsyncId === null) {
|
||||
triggerAsyncId = getDefaultTriggerAsyncId();
|
||||
}
|
||||
|
||||
emitInitNative(asyncId, type, triggerAsyncId, resource);
|
||||
}
|
||||
export { emitInitScript as emitInit };
|
||||
|
||||
export function hasAsyncIdStack() {
|
||||
return hasHooks(kStackLength);
|
||||
}
|
||||
|
||||
export { constants };
|
||||
|
||||
type Fn = (...args: unknown[]) => unknown;
|
||||
|
||||
export class AsyncHook {
|
||||
[init_symbol]: Fn;
|
||||
[before_symbol]: Fn;
|
||||
[after_symbol]: Fn;
|
||||
[destroy_symbol]: Fn;
|
||||
[promise_resolve_symbol]: Fn;
|
||||
|
||||
constructor({
|
||||
init,
|
||||
before,
|
||||
after,
|
||||
destroy,
|
||||
promiseResolve,
|
||||
}: {
|
||||
init: Fn;
|
||||
before: Fn;
|
||||
after: Fn;
|
||||
destroy: Fn;
|
||||
promiseResolve: Fn;
|
||||
}) {
|
||||
if (init !== undefined && typeof init !== "function") {
|
||||
throw new ERR_ASYNC_CALLBACK("hook.init");
|
||||
}
|
||||
if (before !== undefined && typeof before !== "function") {
|
||||
throw new ERR_ASYNC_CALLBACK("hook.before");
|
||||
}
|
||||
if (after !== undefined && typeof after !== "function") {
|
||||
throw new ERR_ASYNC_CALLBACK("hook.after");
|
||||
}
|
||||
if (destroy !== undefined && typeof destroy !== "function") {
|
||||
throw new ERR_ASYNC_CALLBACK("hook.destroy");
|
||||
}
|
||||
if (promiseResolve !== undefined && typeof promiseResolve !== "function") {
|
||||
throw new ERR_ASYNC_CALLBACK("hook.promiseResolve");
|
||||
}
|
||||
|
||||
this[init_symbol] = init;
|
||||
this[before_symbol] = before;
|
||||
this[after_symbol] = after;
|
||||
this[destroy_symbol] = destroy;
|
||||
this[promise_resolve_symbol] = promiseResolve;
|
||||
}
|
||||
|
||||
enable() {
|
||||
// The set of callbacks for a hook should be the same regardless of whether
|
||||
// enable()/disable() are run during their execution. The following
|
||||
// references are reassigned to the tmp arrays if a hook is currently being
|
||||
// processed.
|
||||
// deno-lint-ignore camelcase
|
||||
const { 0: hooks_array, 1: hook_fields } = getHookArrays();
|
||||
|
||||
// Each hook is only allowed to be added once.
|
||||
if (hooks_array.includes(this)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// deno-lint-ignore camelcase
|
||||
const prev_kTotals = hook_fields[kTotals];
|
||||
|
||||
// createHook() has already enforced that the callbacks are all functions,
|
||||
// so here simply increment the count of whether each callbacks exists or
|
||||
// not.
|
||||
hook_fields[kTotals] = hook_fields[kInit] += +!!this[init_symbol];
|
||||
hook_fields[kTotals] += hook_fields[kBefore] += +!!this[before_symbol];
|
||||
hook_fields[kTotals] += hook_fields[kAfter] += +!!this[after_symbol];
|
||||
hook_fields[kTotals] += hook_fields[kDestroy] += +!!this[destroy_symbol];
|
||||
hook_fields[kTotals] += hook_fields[kPromiseResolve] +=
|
||||
+!!this[promise_resolve_symbol];
|
||||
hooks_array.push(this);
|
||||
|
||||
if (prev_kTotals === 0 && hook_fields[kTotals] > 0) {
|
||||
enableHooks();
|
||||
}
|
||||
|
||||
// TODO(kt3k): Uncomment the below
|
||||
// updatePromiseHookMode();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
disable() {
|
||||
// deno-lint-ignore camelcase
|
||||
const { 0: hooks_array, 1: hook_fields } = getHookArrays();
|
||||
|
||||
const index = hooks_array.indexOf(this);
|
||||
if (index === -1) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// deno-lint-ignore camelcase
|
||||
const prev_kTotals = hook_fields[kTotals];
|
||||
|
||||
hook_fields[kTotals] = hook_fields[kInit] -= +!!this[init_symbol];
|
||||
hook_fields[kTotals] += hook_fields[kBefore] -= +!!this[before_symbol];
|
||||
hook_fields[kTotals] += hook_fields[kAfter] -= +!!this[after_symbol];
|
||||
hook_fields[kTotals] += hook_fields[kDestroy] -= +!!this[destroy_symbol];
|
||||
hook_fields[kTotals] += hook_fields[kPromiseResolve] -=
|
||||
+!!this[promise_resolve_symbol];
|
||||
hooks_array.splice(index, 1);
|
||||
|
||||
if (prev_kTotals > 0 && hook_fields[kTotals] === 0) {
|
||||
disableHooks();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue