Merge commit '6e92d2345e8231a44556ce7d416451f5f3e6c398' as 'engine262'

This commit is contained in:
2026-03-27 10:07:29 +05:30
433 changed files with 90478 additions and 0 deletions
+477
View File
@@ -0,0 +1,477 @@
import { ObjectValue, Value, type PropertyKeyValue } from './value.mts';
import {
surroundingAgent,
ScriptEvaluation,
type Markable,
} from './host-defined/engine.mts';
import { HostEnqueueFinalizationRegistryCleanupJob } from './execution-context/WeakReference.mts';
import { AgentSignifier } from './execution-context/Agent.mts';
import { ExecutionContext } from './execution-context/ExecutionContext.mts';
import {
X,
ThrowCompletion,
AbruptCompletion,
type PlainCompletion,
type ValueCompletion,
NormalCompletion,
Q,
} from './completion.mts';
import {
ParseScript,
ParseModule,
ParseJSONModule,
ScriptRecord,
type ParseScriptHostDefined,
} from './parse.mts';
import {
AbstractModuleRecord, ModuleRecord, SourceTextModuleRecord, type ModuleRecordHostDefined, type ModuleRecordHostDefinedPublic,
} from './modules.mts';
import { isWeakRef, type WeakRefObject } from './intrinsics/WeakRef.mts';
import { isFinalizationRegistryObject, type FinalizationRegistryObject } from './intrinsics/FinalizationRegistry.mts';
import { isWeakMapObject, type WeakMapObject } from './intrinsics/WeakMap.mts';
import { isWeakSetObject, type WeakSetObject } from './intrinsics/WeakSet.mts';
import type { PromiseObject } from './intrinsics/Promise.mts';
import type { ParseNode } from './parser/ParseNode.mts';
import {
ClearKeptObjects,
CreateIntrinsics,
SetDefaultGlobalBindings,
OrdinaryObjectCreate,
Assert,
} from '#self';
import {
Realm,
EnsureCompletion, GetModuleNamespace, GlobalEnvironmentRecord, type Intrinsics,
type ValueEvaluator,
} from '#self';
/** https://tc39.es/ecma262/#sec-weakref-execution */
export function gc() {
// At any time, if a set of objects S is not live, an ECMAScript implementation may perform the following steps atomically:
// 1. For each obj of S, do
// a. For each WeakRef ref such that ref.[[WeakRefTarget]] is obj,
// i. Set ref.[[WeakRefTarget]] to empty.
// b. For each FinalizationRegistry fg such that fg.[[Cells]] contains cell, and cell.[[WeakRefTarget]] is obj,
// i. Set cell.[[WeakRefTarget]] to empty.
// ii. Optionally, perform ! HostEnqueueFinalizationRegistryCleanupJob(fg).
// c. For each WeakMap map such that map.WeakMapData contains a record r such that r.Key is obj,
// i. Set r.[[Key]] to empty.
// ii. Set r.[[Value]] to empty.
// d. For each WeakSet set such that set.[[WeakSetData]] contains obj,
// i. Replace the element of set whose value is obj with an element whose value is empty.
const marked = new Set<unknown>();
const weakrefs = new Set<WeakRefObject>();
const fgs = new Set<FinalizationRegistryObject>();
const weakmaps = new Set<WeakMapObject>();
const weaksets = new Set<WeakSetObject>();
const ephemeronQueue: WeakMapObject['WeakMapData'][number][] = [];
const markCb = (O: unknown) => {
if (typeof O !== 'object' || O === null) {
return;
}
if (marked.has(O)) {
return;
}
marked.add(O);
if (isWeakRef(O)) {
weakrefs.add(O);
markCb(O.properties);
markCb(O.Prototype);
} else if (isFinalizationRegistryObject(O)) {
fgs.add(O);
markCb(O.properties);
markCb(O.Prototype);
O.Cells.forEach((cell) => {
markCb(cell.HeldValue);
});
} else if (isWeakMapObject(O)) {
weakmaps.add(O);
markCb(O.properties);
markCb(O.Prototype);
O.WeakMapData.forEach((r) => {
ephemeronQueue.push(r);
});
} else if (isWeakSetObject(O)) {
weaksets.add(O);
markCb(O.properties);
markCb(O.Prototype);
} else if ('mark' in O) {
(O as Markable).mark(markCb);
}
};
markCb(surroundingAgent);
while (ephemeronQueue.length > 0) {
const item = ephemeronQueue.shift()!;
if (marked.has(item.Key)) {
markCb(item.Value);
}
}
weakrefs.forEach((ref) => {
if (!marked.has(ref.WeakRefTarget)) {
ref.WeakRefTarget = undefined;
}
});
fgs.forEach((fg) => {
let dirty = false;
fg.Cells.forEach((cell) => {
if (!marked.has(cell.WeakRefTarget)) {
cell.WeakRefTarget = undefined;
dirty = true;
}
});
if (dirty) {
X(HostEnqueueFinalizationRegistryCleanupJob(fg));
}
});
weakmaps.forEach((map) => {
map.WeakMapData.forEach((r) => {
if (!marked.has(r.Key)) {
r.Key = undefined;
r.Value = undefined;
}
});
});
weaksets.forEach((set) => {
set.WeakSetData.forEach((obj, i) => {
if (!marked.has(obj)) {
set.WeakSetData[i] = undefined;
}
});
});
}
/** https://tc39.es/ecma262/#sec-jobs */
export function runJobQueue() {
if (surroundingAgent.executionContextStack.some((e) => e.ScriptOrModule !== Value.null)) {
return;
}
// At some future point in time, when there is no running execution context
// and the execution context stack is empty, the implementation must:
while (surroundingAgent.jobQueue.length > 0) { // eslint-disable-line no-constant-condition
const {
job: abstractClosure,
callerRealm,
callerScriptOrModule,
} = surroundingAgent.jobQueue.shift()!;
// 1. Perform any implementation-defined preparation steps.
const newContext = new ExecutionContext();
surroundingAgent.executionContextStack.push(newContext);
newContext.Function = Value.null;
newContext.Realm = callerRealm;
newContext.ScriptOrModule = callerScriptOrModule;
// 2. Call the abstract closure.
X(abstractClosure());
// 3. Perform any host-defined cleanup steps, after which the execution context stack must be empty.
ClearKeptObjects();
gc();
surroundingAgent.executionContextStack.pop(newContext);
}
}
export interface ManagedRealmHostDefined {
promiseRejectionTracker?(promise: PromiseObject, operation: 'reject' | 'handle'): void;
getImportMetaProperties?(module: ModuleRecordHostDefinedPublic): readonly { readonly Key: PropertyKeyValue, readonly Value: Value }[];
finalizeImportMeta?(meta: ObjectValue, module: ModuleRecordHostDefinedPublic): PlainCompletion<void>;
resolverCache?: Map<string, AbstractModuleRecord>;
randomSeed?(): string;
attachingInspector?: unknown;
attachingInspectorReportError?(realm: Realm, error: Value): void;
/**
* See https://tc39.es/ecma262/#sec-HostLoadImportedModule
* In case of
* <button type="button" onclick="import('./foo.mjs')">Click me</button>
* and
* new ShadowRealm().importValue('./foo.mjs', 'default')
* a Realm instead of a ModuleRecord or ScriptRecord is passed as the referrer.
*/
specifier?: string | undefined;
/** The name displayed in the inspector. */
name?: string | undefined;
}
export class ManagedRealm extends Realm {
override TemplateMap: { Site: ParseNode.TemplateLiteral; Array: ObjectValue; }[];
override AgentSignifier: unknown;
override Intrinsics: Intrinsics;
override randomState: BigUint64Array<ArrayBufferLike> | undefined;
override GlobalObject: ObjectValue;
override GlobalEnv: GlobalEnvironmentRecord;
override HostDefined: ManagedRealmHostDefined;
topContext: ExecutionContext;
active = false;
/** https://tc39.es/ecma262/#sec-initializehostdefinedrealm */
constructor(HostDefined: ManagedRealmHostDefined = {}, customizations?: (record: Realm) => [global: ObjectValue | undefined, thisValue: ObjectValue | undefined]) {
super();
this.Intrinsics = CreateIntrinsics(this);
this.AgentSignifier = AgentSignifier();
this.TemplateMap = [];
let [global, thisValue] = customizations?.(this) || [];
if (!global) {
global = OrdinaryObjectCreate(this.Intrinsics['%Object.prototype%']);
} else {
Assert(global instanceof ObjectValue);
}
if (!thisValue) {
thisValue = global;
} else {
Assert(thisValue instanceof ObjectValue);
}
this.GlobalObject = global;
this.GlobalEnv = new GlobalEnvironmentRecord(global, thisValue);
SetDefaultGlobalBindings(this);
const newContext = new ExecutionContext();
newContext.Function = Value.null;
newContext.Realm = this;
newContext.ScriptOrModule = Value.null;
this.HostDefined = HostDefined;
this.topContext = newContext;
surroundingAgent.hostDefinedOptions.onRealmCreated?.(this);
}
scope(inspectorPreview?: boolean): Disposable | null;
scope<T>(cb: () => T, inspectorPreview?: boolean): T
scope<T>(arg0?: (() => T) | boolean, arg2?: boolean): T | Disposable | null {
if (typeof arg0 !== 'function') {
const inspectorPreview = arg0;
if (this.active) {
return null;
}
this.active = true;
surroundingAgent.executionContextStack.push(this.topContext);
using _ = inspectorPreview ? surroundingAgent.debugger_scopePreview() : null;
return {
[Symbol.dispose]: () => {
surroundingAgent.executionContextStack.pop(this.topContext);
this.active = false;
},
};
} else {
const callback = arg0;
if (this.active) {
return arg0();
}
this.active = true;
surroundingAgent.executionContextStack.push(this.topContext);
const result = arg2 ? surroundingAgent.debugger_scopePreview(callback) : callback();
surroundingAgent.executionContextStack.pop(this.topContext);
this.active = false;
return result;
}
}
compileScript(sourceText: string, hostDefined?: ParseScriptHostDefined): PlainCompletion<ScriptRecord> {
return this.scope(() => {
const s = ParseScript(sourceText, this, hostDefined);
if (Array.isArray(s)) {
return ThrowCompletion(s[0]);
}
return NormalCompletion(s);
});
}
compileModule(sourceText: string, hostDefined?: ModuleRecordHostDefined) {
return this.scope(() => {
const s = ParseModule(sourceText, this, {
SourceTextModuleRecord: ManagedSourceTextModuleRecord,
...hostDefined,
});
if (Array.isArray(s)) {
return ThrowCompletion(s[0]);
}
return NormalCompletion(s);
});
}
/**
* Call surroundingAgent.resumeEvaluate() to continue evaluation.
*
* This function will synchronously return a completion if this is a nested evaluation and debugger cannot be triggered.
*/
evaluate(sourceText: ScriptRecord | ModuleRecord | ValueEvaluator, callback: (completion: NormalCompletion<Value> | ThrowCompletion) => void) {
if (!sourceText) {
throw new TypeError('sourceText is null or undefined');
}
let result: ValueCompletion | undefined;
if (sourceText instanceof ModuleRecord) {
const old = this.active;
this.active = true;
surroundingAgent.executionContextStack.push(this.topContext);
const loadModuleCompletion = sourceText.LoadRequestedModules();
const link = ((): PlainCompletion<void> => {
if (loadModuleCompletion.PromiseState === 'rejected') {
Q(ThrowCompletion(loadModuleCompletion.PromiseResult!));
} else if (loadModuleCompletion.PromiseState === 'pending') {
throw new Error('Internal error: .LoadRequestedModules() returned a pending promise');
}
Q(sourceText.Link());
})();
if (link instanceof ThrowCompletion) {
callback(link);
return link;
}
surroundingAgent.evaluate(sourceText.Evaluate(), (completion) => {
if (completion instanceof NormalCompletion && completion.Value.PromiseState === 'fulfilled') {
result = GetModuleNamespace(sourceText, 'evaluation');
} else {
result = completion;
}
this.active = old;
surroundingAgent.executionContextStack.pop(this.topContext);
callback(EnsureCompletion(result));
});
return result;
} else if (sourceText instanceof ScriptRecord) {
const old = this.active;
this.active = true;
surroundingAgent.executionContextStack.push(this.topContext);
surroundingAgent.evaluate(ScriptEvaluation(sourceText), (completion) => {
this.active = old;
surroundingAgent.executionContextStack.pop(this.topContext);
result = completion;
callback(completion);
});
return result;
} else {
// this path only called by the inspector
Assert(!!surroundingAgent.hostDefinedOptions.onDebugger);
let emptyExecutionStack = false;
if (!surroundingAgent.runningExecutionContext) {
emptyExecutionStack = true;
this.active = true;
surroundingAgent.executionContextStack.push(this.topContext);
}
surroundingAgent.evaluate(sourceText, (completion) => {
result = completion;
if (emptyExecutionStack) {
this.active = false;
surroundingAgent.executionContextStack.pop(this.topContext);
}
callback(completion);
});
return result;
}
}
evaluateScript(sourceText: string | ScriptRecord, { specifier, doNotTrackScriptId }: { specifier?: string, doNotTrackScriptId?: boolean } = {}): ValueCompletion {
if (sourceText === undefined || sourceText === null) {
throw new TypeError('sourceText must be a string or a ScriptRecord');
}
if (typeof sourceText === 'string') {
sourceText = Q(this.compileScript(sourceText, { specifier, doNotTrackScriptId }));
}
let completion;
completion = this.evaluate(sourceText, (c) => {
completion = c;
});
if (!completion) {
surroundingAgent.resumeEvaluate({
noBreakpoint: true,
});
}
if (!completion) {
throw new Assert.Error('Expect evaluation completes synchronously');
}
if (!(completion instanceof AbruptCompletion)) {
runJobQueue();
}
return completion;
}
evaluateModule(sourceText: string, specifier: string): PlainCompletion<SourceTextModuleRecord>
evaluateModule<T extends ModuleRecord>(sourceText: T, specifier: string): PlainCompletion<T>
evaluateModule(sourceText: string | ModuleRecord, specifier: string): PlainCompletion<ModuleRecord> {
if (sourceText === undefined || sourceText === null) {
throw new TypeError('sourceText must be a string or a ModuleRecord');
}
if (typeof sourceText === 'string') {
sourceText = Q(this.compileModule(sourceText, { specifier }));
}
let completion;
completion = this.evaluate(sourceText, (c) => {
completion = c;
if (!(completion instanceof AbruptCompletion)) {
runJobQueue();
}
});
if (!completion) {
surroundingAgent.resumeEvaluate({
noBreakpoint: true,
});
}
return sourceText;
}
/**
* @deprecated use compileModule
*/
createSourceTextModule(specifier: string, sourceText: string): PlainCompletion<SourceTextModuleRecord> {
if (typeof specifier !== 'string') {
throw new TypeError('specifier must be a string');
}
if (typeof sourceText !== 'string') {
throw new TypeError('sourceText must be a string');
}
const module = this.scope(() => ParseModule(sourceText, this, {
specifier,
SourceTextModuleRecord: ManagedSourceTextModuleRecord,
}));
if (Array.isArray(module)) {
return ThrowCompletion(module[0]);
}
return module;
}
createJSONModule(specifier: string, sourceText: string) {
if (typeof specifier !== 'string') {
throw new TypeError('specifier must be a string');
}
if (typeof sourceText !== 'string') {
throw new TypeError('sourceText must be a string');
}
const module = this.scope(() => ParseJSONModule(Value(sourceText), this, {
specifier,
}));
return module;
}
}
class ManagedSourceTextModuleRecord extends SourceTextModuleRecord {
override* Evaluate() {
const r = yield* super.Evaluate();
runJobQueue();
return r;
}
}