jslike 1.4.5 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +51 -25
- package/dist/esm/interpreter/index.js +2 -1
- package/dist/esm/interpreter/interpreter.js +85 -26
- package/dist/esm/runtime/execution-controller.js +212 -0
- package/dist/index.cjs +287 -35
- package/dist/index.d.cts +350 -52
- package/dist/index.d.ts +350 -52
- package/dist/index.js +286 -35
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -6440,10 +6440,17 @@ class Interpreter {
|
|
|
6440
6440
|
this.moduleCache = new Map(); // Cache loaded modules
|
|
6441
6441
|
this.moduleExports = {}; // Track exports in current module
|
|
6442
6442
|
this.abortSignal = options.abortSignal;
|
|
6443
|
+
this.executionController = options.executionController;
|
|
6443
6444
|
}
|
|
6444
6445
|
|
|
6445
|
-
// Check if execution should be aborted
|
|
6446
|
+
// Check if execution should be aborted (sync version)
|
|
6446
6447
|
checkAbortSignal() {
|
|
6448
|
+
// Check controller first if available
|
|
6449
|
+
if (this.executionController) {
|
|
6450
|
+
this.executionController._checkAbortSync();
|
|
6451
|
+
return;
|
|
6452
|
+
}
|
|
6453
|
+
// Fall back to legacy abortSignal
|
|
6447
6454
|
if (this.abortSignal && this.abortSignal.aborted) {
|
|
6448
6455
|
const error = new Error('The operation was aborted');
|
|
6449
6456
|
error.name = 'AbortError';
|
|
@@ -6451,12 +6458,26 @@ class Interpreter {
|
|
|
6451
6458
|
}
|
|
6452
6459
|
}
|
|
6453
6460
|
|
|
6461
|
+
// Checkpoint that returns a promise only when controller is present
|
|
6462
|
+
// When no controller, returns null to signal no await needed
|
|
6463
|
+
_getCheckpointPromise(node, env) {
|
|
6464
|
+
if (this.executionController) {
|
|
6465
|
+
this.executionController._setEnv(env);
|
|
6466
|
+
return this.executionController._checkpoint(node);
|
|
6467
|
+
} else {
|
|
6468
|
+
this.checkAbortSignal();
|
|
6469
|
+
return null; // Signal that no await is needed
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
6472
|
+
|
|
6454
6473
|
// Async evaluation for async functions - handles await expressions
|
|
6455
6474
|
async evaluateAsync(node, env) {
|
|
6456
6475
|
if (!node) return undefined;
|
|
6457
6476
|
|
|
6458
|
-
//
|
|
6459
|
-
|
|
6477
|
+
// Checkpoint - yields if paused, throws if aborted
|
|
6478
|
+
// Only await when there's actually a promise (controller present)
|
|
6479
|
+
const checkpointPromise = this._getCheckpointPromise(node, env);
|
|
6480
|
+
if (checkpointPromise) await checkpointPromise;
|
|
6460
6481
|
|
|
6461
6482
|
// Handle await expressions by actually awaiting the promise
|
|
6462
6483
|
if (node.type === 'AwaitExpression') {
|
|
@@ -6693,6 +6714,9 @@ class Interpreter {
|
|
|
6693
6714
|
await this.evaluateAsync(node.init, forEnv);
|
|
6694
6715
|
}
|
|
6695
6716
|
while (!node.test || await this.evaluateAsync(node.test, forEnv)) {
|
|
6717
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
6718
|
+
const cp1 = this._getCheckpointPromise(node, forEnv);
|
|
6719
|
+
if (cp1) await cp1;
|
|
6696
6720
|
const result = await this.evaluateAsync(node.body, forEnv);
|
|
6697
6721
|
if (result instanceof BreakSignal) {
|
|
6698
6722
|
break;
|
|
@@ -6721,6 +6745,9 @@ class Interpreter {
|
|
|
6721
6745
|
const isConst = node.left.kind === 'const';
|
|
6722
6746
|
|
|
6723
6747
|
for (const value of iterable) {
|
|
6748
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
6749
|
+
const cp2 = this._getCheckpointPromise(node, forEnv);
|
|
6750
|
+
if (cp2) await cp2;
|
|
6724
6751
|
const iterEnv = forEnv.extend();
|
|
6725
6752
|
if (declarator.id.type === 'Identifier') {
|
|
6726
6753
|
iterEnv.define(declarator.id.name, value, isConst);
|
|
@@ -6754,6 +6781,9 @@ class Interpreter {
|
|
|
6754
6781
|
forEnv.define(varName, undefined);
|
|
6755
6782
|
|
|
6756
6783
|
for (const key in obj) {
|
|
6784
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
6785
|
+
const cp3 = this._getCheckpointPromise(node, forEnv);
|
|
6786
|
+
if (cp3) await cp3;
|
|
6757
6787
|
forEnv.set(varName, key);
|
|
6758
6788
|
const result = await this.evaluateAsync(node.body, forEnv);
|
|
6759
6789
|
if (result instanceof BreakSignal) {
|
|
@@ -6772,6 +6802,9 @@ class Interpreter {
|
|
|
6772
6802
|
// For WhileStatement with async body
|
|
6773
6803
|
if (node.type === 'WhileStatement') {
|
|
6774
6804
|
while (await this.evaluateAsync(node.test, env)) {
|
|
6805
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
6806
|
+
const cp4 = this._getCheckpointPromise(node, env);
|
|
6807
|
+
if (cp4) await cp4;
|
|
6775
6808
|
const result = await this.evaluateAsync(node.body, env);
|
|
6776
6809
|
if (result instanceof BreakSignal) {
|
|
6777
6810
|
break;
|
|
@@ -6789,6 +6822,9 @@ class Interpreter {
|
|
|
6789
6822
|
// For DoWhileStatement with async body
|
|
6790
6823
|
if (node.type === 'DoWhileStatement') {
|
|
6791
6824
|
do {
|
|
6825
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
6826
|
+
const cp5 = this._getCheckpointPromise(node, env);
|
|
6827
|
+
if (cp5) await cp5;
|
|
6792
6828
|
const result = await this.evaluateAsync(node.body, env);
|
|
6793
6829
|
if (result instanceof BreakSignal) {
|
|
6794
6830
|
break;
|
|
@@ -7592,6 +7628,9 @@ class Interpreter {
|
|
|
7592
7628
|
const metadata = func.__metadata || func;
|
|
7593
7629
|
const funcEnv = new Environment(metadata.closure);
|
|
7594
7630
|
|
|
7631
|
+
// Get function name for call stack tracking
|
|
7632
|
+
const funcName = metadata.name || func.name || 'anonymous';
|
|
7633
|
+
|
|
7595
7634
|
// Bind 'this' if provided (for method calls)
|
|
7596
7635
|
if (thisContext !== undefined) {
|
|
7597
7636
|
funcEnv.define('this', thisContext);
|
|
@@ -7627,18 +7666,54 @@ class Interpreter {
|
|
|
7627
7666
|
// Execute function body
|
|
7628
7667
|
// If async, use async evaluation and return a promise
|
|
7629
7668
|
if (metadata.async) {
|
|
7669
|
+
// Track call stack for async functions
|
|
7670
|
+
if (this.executionController) {
|
|
7671
|
+
this.executionController._pushCall(funcName);
|
|
7672
|
+
}
|
|
7630
7673
|
return (async () => {
|
|
7674
|
+
try {
|
|
7675
|
+
if (metadata.expression) {
|
|
7676
|
+
// Arrow function with expression body
|
|
7677
|
+
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
7678
|
+
// If the result is a ThrowSignal, throw the error
|
|
7679
|
+
if (result instanceof ThrowSignal) {
|
|
7680
|
+
throw result.value;
|
|
7681
|
+
}
|
|
7682
|
+
return result;
|
|
7683
|
+
} else {
|
|
7684
|
+
// Block statement body
|
|
7685
|
+
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
7686
|
+
if (result instanceof ReturnValue) {
|
|
7687
|
+
return result.value;
|
|
7688
|
+
}
|
|
7689
|
+
// If the result is a ThrowSignal, throw the error
|
|
7690
|
+
if (result instanceof ThrowSignal) {
|
|
7691
|
+
throw result.value;
|
|
7692
|
+
}
|
|
7693
|
+
return undefined;
|
|
7694
|
+
}
|
|
7695
|
+
} finally {
|
|
7696
|
+
if (this.executionController) {
|
|
7697
|
+
this.executionController._popCall();
|
|
7698
|
+
}
|
|
7699
|
+
}
|
|
7700
|
+
})();
|
|
7701
|
+
} else {
|
|
7702
|
+
// Synchronous evaluation for non-async functions
|
|
7703
|
+
// Track call stack for sync functions
|
|
7704
|
+
if (this.executionController) {
|
|
7705
|
+
this.executionController._pushCall(funcName);
|
|
7706
|
+
}
|
|
7707
|
+
try {
|
|
7631
7708
|
if (metadata.expression) {
|
|
7632
|
-
|
|
7633
|
-
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
7709
|
+
const result = this.evaluate(metadata.body, funcEnv);
|
|
7634
7710
|
// If the result is a ThrowSignal, throw the error
|
|
7635
7711
|
if (result instanceof ThrowSignal) {
|
|
7636
7712
|
throw result.value;
|
|
7637
7713
|
}
|
|
7638
7714
|
return result;
|
|
7639
7715
|
} else {
|
|
7640
|
-
|
|
7641
|
-
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
7716
|
+
const result = this.evaluate(metadata.body, funcEnv);
|
|
7642
7717
|
if (result instanceof ReturnValue) {
|
|
7643
7718
|
return result.value;
|
|
7644
7719
|
}
|
|
@@ -7648,26 +7723,10 @@ class Interpreter {
|
|
|
7648
7723
|
}
|
|
7649
7724
|
return undefined;
|
|
7650
7725
|
}
|
|
7651
|
-
}
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
if (metadata.expression) {
|
|
7655
|
-
const result = this.evaluate(metadata.body, funcEnv);
|
|
7656
|
-
// If the result is a ThrowSignal, throw the error
|
|
7657
|
-
if (result instanceof ThrowSignal) {
|
|
7658
|
-
throw result.value;
|
|
7659
|
-
}
|
|
7660
|
-
return result;
|
|
7661
|
-
} else {
|
|
7662
|
-
const result = this.evaluate(metadata.body, funcEnv);
|
|
7663
|
-
if (result instanceof ReturnValue) {
|
|
7664
|
-
return result.value;
|
|
7665
|
-
}
|
|
7666
|
-
// If the result is a ThrowSignal, throw the error
|
|
7667
|
-
if (result instanceof ThrowSignal) {
|
|
7668
|
-
throw result.value;
|
|
7726
|
+
} finally {
|
|
7727
|
+
if (this.executionController) {
|
|
7728
|
+
this.executionController._popCall();
|
|
7669
7729
|
}
|
|
7670
|
-
return undefined;
|
|
7671
7730
|
}
|
|
7672
7731
|
}
|
|
7673
7732
|
}
|
|
@@ -9228,7 +9287,8 @@ class WangInterpreter {
|
|
|
9228
9287
|
|
|
9229
9288
|
// Prepare execution options
|
|
9230
9289
|
const options = {
|
|
9231
|
-
moduleResolver: this.moduleResolver
|
|
9290
|
+
moduleResolver: this.moduleResolver,
|
|
9291
|
+
executionController: userOptions.executionController
|
|
9232
9292
|
// sourceType will be auto-detected from code
|
|
9233
9293
|
};
|
|
9234
9294
|
|
|
@@ -9327,6 +9387,219 @@ class InMemoryModuleResolver {
|
|
|
9327
9387
|
}
|
|
9328
9388
|
}
|
|
9329
9389
|
|
|
9390
|
+
/**
|
|
9391
|
+
* ExecutionController - Controls and monitors interpreter execution
|
|
9392
|
+
*
|
|
9393
|
+
* Provides pause/resume/abort capabilities and execution status introspection.
|
|
9394
|
+
* Works with async evaluation only - sync evaluation can only abort.
|
|
9395
|
+
*/
|
|
9396
|
+
class ExecutionController {
|
|
9397
|
+
constructor() {
|
|
9398
|
+
this.state = 'idle'; // 'idle' | 'running' | 'paused' | 'aborted' | 'completed'
|
|
9399
|
+
this.pauseRequested = false;
|
|
9400
|
+
this.abortRequested = false;
|
|
9401
|
+
|
|
9402
|
+
// Debug info
|
|
9403
|
+
this.stepCount = 0;
|
|
9404
|
+
this.currentNode = null;
|
|
9405
|
+
this.callStack = [];
|
|
9406
|
+
this.currentEnv = null;
|
|
9407
|
+
|
|
9408
|
+
this._resolveResume = null;
|
|
9409
|
+
}
|
|
9410
|
+
|
|
9411
|
+
// --- Control methods (called by user) ---
|
|
9412
|
+
|
|
9413
|
+
/**
|
|
9414
|
+
* Request pause at next checkpoint. Only works during async evaluation.
|
|
9415
|
+
*/
|
|
9416
|
+
pause() {
|
|
9417
|
+
if (this.state === 'running') {
|
|
9418
|
+
this.pauseRequested = true;
|
|
9419
|
+
}
|
|
9420
|
+
}
|
|
9421
|
+
|
|
9422
|
+
/**
|
|
9423
|
+
* Resume execution after pause.
|
|
9424
|
+
*/
|
|
9425
|
+
resume() {
|
|
9426
|
+
if (this.state === 'paused' && this._resolveResume) {
|
|
9427
|
+
this.pauseRequested = false;
|
|
9428
|
+
this._resolveResume();
|
|
9429
|
+
this._resolveResume = null;
|
|
9430
|
+
}
|
|
9431
|
+
}
|
|
9432
|
+
|
|
9433
|
+
/**
|
|
9434
|
+
* Abort execution. Works for both sync and async evaluation.
|
|
9435
|
+
* If paused, will resume and then abort.
|
|
9436
|
+
*/
|
|
9437
|
+
abort() {
|
|
9438
|
+
this.abortRequested = true;
|
|
9439
|
+
// Also resume if paused to allow abort to take effect
|
|
9440
|
+
if (this._resolveResume) {
|
|
9441
|
+
this._resolveResume();
|
|
9442
|
+
this._resolveResume = null;
|
|
9443
|
+
}
|
|
9444
|
+
}
|
|
9445
|
+
|
|
9446
|
+
// --- Status (called by user) ---
|
|
9447
|
+
|
|
9448
|
+
/**
|
|
9449
|
+
* Get current execution status with full debug information.
|
|
9450
|
+
* @returns {Object} Status object with state, stepCount, currentNode, callStack, and variables
|
|
9451
|
+
*/
|
|
9452
|
+
getStatus() {
|
|
9453
|
+
return {
|
|
9454
|
+
state: this.state,
|
|
9455
|
+
stepCount: this.stepCount,
|
|
9456
|
+
currentNode: this.currentNode?.type || null,
|
|
9457
|
+
callStack: [...this.callStack],
|
|
9458
|
+
variables: this._getEnvironmentVariables()
|
|
9459
|
+
};
|
|
9460
|
+
}
|
|
9461
|
+
|
|
9462
|
+
/**
|
|
9463
|
+
* Check if abort has been requested.
|
|
9464
|
+
* Compatible with AbortSignal interface.
|
|
9465
|
+
*/
|
|
9466
|
+
get aborted() {
|
|
9467
|
+
return this.abortRequested;
|
|
9468
|
+
}
|
|
9469
|
+
|
|
9470
|
+
// --- Internal methods (called by interpreter) ---
|
|
9471
|
+
|
|
9472
|
+
/**
|
|
9473
|
+
* Mark execution as started. Called by execute().
|
|
9474
|
+
* @internal
|
|
9475
|
+
*/
|
|
9476
|
+
_start() {
|
|
9477
|
+
this.state = 'running';
|
|
9478
|
+
this.stepCount = 0;
|
|
9479
|
+
this.currentNode = null;
|
|
9480
|
+
this.callStack = [];
|
|
9481
|
+
this.pauseRequested = false;
|
|
9482
|
+
// Note: don't reset abortRequested - allow pre-abort
|
|
9483
|
+
}
|
|
9484
|
+
|
|
9485
|
+
/**
|
|
9486
|
+
* Mark execution as completed. Called by execute().
|
|
9487
|
+
* @internal
|
|
9488
|
+
*/
|
|
9489
|
+
_complete() {
|
|
9490
|
+
this.state = 'completed';
|
|
9491
|
+
}
|
|
9492
|
+
|
|
9493
|
+
/**
|
|
9494
|
+
* Push a function call onto the call stack.
|
|
9495
|
+
* @internal
|
|
9496
|
+
*/
|
|
9497
|
+
_pushCall(name) {
|
|
9498
|
+
this.callStack.push(name);
|
|
9499
|
+
}
|
|
9500
|
+
|
|
9501
|
+
/**
|
|
9502
|
+
* Pop a function call from the call stack.
|
|
9503
|
+
* @internal
|
|
9504
|
+
*/
|
|
9505
|
+
_popCall() {
|
|
9506
|
+
this.callStack.pop();
|
|
9507
|
+
}
|
|
9508
|
+
|
|
9509
|
+
/**
|
|
9510
|
+
* Set the current environment for variable introspection.
|
|
9511
|
+
* @internal
|
|
9512
|
+
*/
|
|
9513
|
+
_setEnv(env) {
|
|
9514
|
+
this.currentEnv = env;
|
|
9515
|
+
}
|
|
9516
|
+
|
|
9517
|
+
/**
|
|
9518
|
+
* Async checkpoint - yields if paused, throws if aborted.
|
|
9519
|
+
* Called at coarse granularity points (loops, function calls).
|
|
9520
|
+
* @internal
|
|
9521
|
+
*/
|
|
9522
|
+
async _checkpoint(node) {
|
|
9523
|
+
this.stepCount++;
|
|
9524
|
+
this.currentNode = node;
|
|
9525
|
+
|
|
9526
|
+
if (this.abortRequested) {
|
|
9527
|
+
this.state = 'aborted';
|
|
9528
|
+
const error = new Error('The operation was aborted');
|
|
9529
|
+
error.name = 'AbortError';
|
|
9530
|
+
throw error;
|
|
9531
|
+
}
|
|
9532
|
+
|
|
9533
|
+
if (this.pauseRequested && this.state === 'running') {
|
|
9534
|
+
this.state = 'paused';
|
|
9535
|
+
await new Promise(resolve => { this._resolveResume = resolve; });
|
|
9536
|
+
this.state = 'running';
|
|
9537
|
+
|
|
9538
|
+
// Check abort after resume (user may have called abort while paused)
|
|
9539
|
+
if (this.abortRequested) {
|
|
9540
|
+
this.state = 'aborted';
|
|
9541
|
+
const error = new Error('The operation was aborted');
|
|
9542
|
+
error.name = 'AbortError';
|
|
9543
|
+
throw error;
|
|
9544
|
+
}
|
|
9545
|
+
}
|
|
9546
|
+
}
|
|
9547
|
+
|
|
9548
|
+
/**
|
|
9549
|
+
* Sync abort check - only throws if aborted, cannot pause.
|
|
9550
|
+
* Used in sync evaluate() path.
|
|
9551
|
+
* @internal
|
|
9552
|
+
*/
|
|
9553
|
+
_checkAbortSync() {
|
|
9554
|
+
if (this.abortRequested) {
|
|
9555
|
+
this.state = 'aborted';
|
|
9556
|
+
const error = new Error('The operation was aborted');
|
|
9557
|
+
error.name = 'AbortError';
|
|
9558
|
+
throw error;
|
|
9559
|
+
}
|
|
9560
|
+
}
|
|
9561
|
+
|
|
9562
|
+
/**
|
|
9563
|
+
* Get all variables from current environment chain.
|
|
9564
|
+
* @internal
|
|
9565
|
+
*/
|
|
9566
|
+
_getEnvironmentVariables() {
|
|
9567
|
+
if (!this.currentEnv) return {};
|
|
9568
|
+
const vars = {};
|
|
9569
|
+
let env = this.currentEnv;
|
|
9570
|
+
while (env) {
|
|
9571
|
+
if (env.vars) {
|
|
9572
|
+
for (const [key, value] of env.vars) {
|
|
9573
|
+
if (!(key in vars)) {
|
|
9574
|
+
vars[key] = this._serializeValue(value);
|
|
9575
|
+
}
|
|
9576
|
+
}
|
|
9577
|
+
}
|
|
9578
|
+
env = env.parent;
|
|
9579
|
+
}
|
|
9580
|
+
return vars;
|
|
9581
|
+
}
|
|
9582
|
+
|
|
9583
|
+
/**
|
|
9584
|
+
* Serialize a value for status reporting.
|
|
9585
|
+
* @internal
|
|
9586
|
+
*/
|
|
9587
|
+
_serializeValue(value) {
|
|
9588
|
+
if (value === undefined) return { type: 'undefined' };
|
|
9589
|
+
if (value === null) return { type: 'null' };
|
|
9590
|
+
if (typeof value === 'function' || (value && value.__isFunction)) {
|
|
9591
|
+
return { type: 'function', name: value.name || 'anonymous' };
|
|
9592
|
+
}
|
|
9593
|
+
if (Array.isArray(value)) {
|
|
9594
|
+
return { type: 'array', length: value.length };
|
|
9595
|
+
}
|
|
9596
|
+
if (typeof value === 'object') {
|
|
9597
|
+
return { type: 'object', preview: Object.keys(value).slice(0, 5) };
|
|
9598
|
+
}
|
|
9599
|
+
return { type: typeof value, value };
|
|
9600
|
+
}
|
|
9601
|
+
}
|
|
9602
|
+
|
|
9330
9603
|
// Use bundled Acorn parser for zero runtime dependencies
|
|
9331
9604
|
|
|
9332
9605
|
// Helper to detect if code contains module syntax or top-level await
|
|
@@ -9423,34 +9696,59 @@ function containsTopLevelAwait(node) {
|
|
|
9423
9696
|
}
|
|
9424
9697
|
|
|
9425
9698
|
async function execute(code, env = null, options = {}) {
|
|
9426
|
-
//
|
|
9427
|
-
const
|
|
9699
|
+
// Get execution controller if provided
|
|
9700
|
+
const controller = options.executionController;
|
|
9428
9701
|
|
|
9429
|
-
//
|
|
9430
|
-
if (
|
|
9431
|
-
|
|
9702
|
+
// Mark execution as starting
|
|
9703
|
+
if (controller) {
|
|
9704
|
+
controller._start();
|
|
9432
9705
|
}
|
|
9433
9706
|
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
abortSignal: options.abortSignal
|
|
9438
|
-
});
|
|
9707
|
+
try {
|
|
9708
|
+
// Parse the code
|
|
9709
|
+
const ast = parse(code, options);
|
|
9439
9710
|
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9711
|
+
// Create global environment if not provided
|
|
9712
|
+
if (!env) {
|
|
9713
|
+
env = createGlobalEnvironment(new Environment());
|
|
9714
|
+
}
|
|
9715
|
+
|
|
9716
|
+
// Create interpreter with module resolver, abort signal, and execution controller
|
|
9717
|
+
const interpreter = new Interpreter(env, {
|
|
9718
|
+
moduleResolver: options.moduleResolver,
|
|
9719
|
+
abortSignal: options.abortSignal,
|
|
9720
|
+
executionController: controller
|
|
9721
|
+
});
|
|
9722
|
+
|
|
9723
|
+
// Use async evaluation if:
|
|
9724
|
+
// 1. Explicitly requested module mode
|
|
9725
|
+
// 2. AST contains import/export declarations
|
|
9726
|
+
// 3. Code contains top-level await
|
|
9727
|
+
// 4. Execution controller provided (needs async for pause/resume)
|
|
9728
|
+
const needsAsync = options.sourceType === 'module' ||
|
|
9729
|
+
containsModuleDeclarations(ast) ||
|
|
9730
|
+
containsTopLevelAwait(ast) ||
|
|
9731
|
+
controller != null;
|
|
9732
|
+
|
|
9733
|
+
if (needsAsync) {
|
|
9734
|
+
const result = await interpreter.evaluateAsync(ast, env);
|
|
9735
|
+
if (controller) {
|
|
9736
|
+
controller._complete();
|
|
9737
|
+
}
|
|
9738
|
+
return result instanceof ReturnValue ? result.value : result;
|
|
9739
|
+
} else {
|
|
9740
|
+
const result = interpreter.evaluate(ast, env);
|
|
9741
|
+
if (controller) {
|
|
9742
|
+
controller._complete();
|
|
9743
|
+
}
|
|
9744
|
+
return result instanceof ReturnValue ? result.value : result;
|
|
9745
|
+
}
|
|
9746
|
+
} catch (e) {
|
|
9747
|
+
// Mark as aborted if that's the error type
|
|
9748
|
+
if (controller && e.name === 'AbortError') {
|
|
9749
|
+
controller.state = 'aborted';
|
|
9750
|
+
}
|
|
9751
|
+
throw e;
|
|
9454
9752
|
}
|
|
9455
9753
|
}
|
|
9456
9754
|
|
|
@@ -9503,4 +9801,4 @@ class ModuleResolver {
|
|
|
9503
9801
|
* @property {any} [metadata] - Optional metadata about the module
|
|
9504
9802
|
*/
|
|
9505
9803
|
|
|
9506
|
-
export { Environment, InMemoryModuleResolver, Interpreter, ModuleResolver, WangInterpreter, createEnvironment, execute, isTopLevelAwait, parse };
|
|
9804
|
+
export { Environment, ExecutionController, InMemoryModuleResolver, Interpreter, ModuleResolver, WangInterpreter, createEnvironment, execute, isTopLevelAwait, parse };
|