jslike 1.4.5 → 1.6.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 +370 -26
- package/dist/esm/parser.js +781 -3
- package/dist/esm/runtime/builtins.js +14 -0
- package/dist/esm/runtime/execution-controller.js +212 -0
- package/dist/index.cjs +1199 -41
- package/dist/index.d.cts +1394 -63
- package/dist/index.d.ts +1394 -63
- package/dist/index.js +1198 -41
- package/dist/validator/index.cjs +669 -5
- package/dist/validator/index.js +669 -5
- package/package.json +5 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Self-contained Acorn parser (bundled for zero runtime dependencies)
|
|
2
|
+
* Self-contained Acorn parser with JSX support (bundled for zero runtime dependencies)
|
|
3
3
|
* Acorn is MIT licensed: https://github.com/acornjs/acorn
|
|
4
|
+
* Acorn-JSX is MIT licensed: https://github.com/acornjs/acorn-jsx
|
|
4
5
|
* This file is auto-generated by scripts/bundle-acorn.js
|
|
5
|
-
* Original size: ~224KB
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// This file was generated. Do not modify manually!
|
|
@@ -6212,14 +6212,748 @@ Parser.acorn = {
|
|
|
6212
6212
|
nonASCIIwhitespace: nonASCIIwhitespace
|
|
6213
6213
|
};
|
|
6214
6214
|
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6215
|
+
const XHTMLEntities = {
|
|
6216
|
+
quot: '\u0022',
|
|
6217
|
+
amp: '&',
|
|
6218
|
+
apos: '\u0027',
|
|
6219
|
+
lt: '<',
|
|
6220
|
+
gt: '>',
|
|
6221
|
+
nbsp: '\u00A0',
|
|
6222
|
+
iexcl: '\u00A1',
|
|
6223
|
+
cent: '\u00A2',
|
|
6224
|
+
pound: '\u00A3',
|
|
6225
|
+
curren: '\u00A4',
|
|
6226
|
+
yen: '\u00A5',
|
|
6227
|
+
brvbar: '\u00A6',
|
|
6228
|
+
sect: '\u00A7',
|
|
6229
|
+
uml: '\u00A8',
|
|
6230
|
+
copy: '\u00A9',
|
|
6231
|
+
ordf: '\u00AA',
|
|
6232
|
+
laquo: '\u00AB',
|
|
6233
|
+
not: '\u00AC',
|
|
6234
|
+
shy: '\u00AD',
|
|
6235
|
+
reg: '\u00AE',
|
|
6236
|
+
macr: '\u00AF',
|
|
6237
|
+
deg: '\u00B0',
|
|
6238
|
+
plusmn: '\u00B1',
|
|
6239
|
+
sup2: '\u00B2',
|
|
6240
|
+
sup3: '\u00B3',
|
|
6241
|
+
acute: '\u00B4',
|
|
6242
|
+
micro: '\u00B5',
|
|
6243
|
+
para: '\u00B6',
|
|
6244
|
+
middot: '\u00B7',
|
|
6245
|
+
cedil: '\u00B8',
|
|
6246
|
+
sup1: '\u00B9',
|
|
6247
|
+
ordm: '\u00BA',
|
|
6248
|
+
raquo: '\u00BB',
|
|
6249
|
+
frac14: '\u00BC',
|
|
6250
|
+
frac12: '\u00BD',
|
|
6251
|
+
frac34: '\u00BE',
|
|
6252
|
+
iquest: '\u00BF',
|
|
6253
|
+
Agrave: '\u00C0',
|
|
6254
|
+
Aacute: '\u00C1',
|
|
6255
|
+
Acirc: '\u00C2',
|
|
6256
|
+
Atilde: '\u00C3',
|
|
6257
|
+
Auml: '\u00C4',
|
|
6258
|
+
Aring: '\u00C5',
|
|
6259
|
+
AElig: '\u00C6',
|
|
6260
|
+
Ccedil: '\u00C7',
|
|
6261
|
+
Egrave: '\u00C8',
|
|
6262
|
+
Eacute: '\u00C9',
|
|
6263
|
+
Ecirc: '\u00CA',
|
|
6264
|
+
Euml: '\u00CB',
|
|
6265
|
+
Igrave: '\u00CC',
|
|
6266
|
+
Iacute: '\u00CD',
|
|
6267
|
+
Icirc: '\u00CE',
|
|
6268
|
+
Iuml: '\u00CF',
|
|
6269
|
+
ETH: '\u00D0',
|
|
6270
|
+
Ntilde: '\u00D1',
|
|
6271
|
+
Ograve: '\u00D2',
|
|
6272
|
+
Oacute: '\u00D3',
|
|
6273
|
+
Ocirc: '\u00D4',
|
|
6274
|
+
Otilde: '\u00D5',
|
|
6275
|
+
Ouml: '\u00D6',
|
|
6276
|
+
times: '\u00D7',
|
|
6277
|
+
Oslash: '\u00D8',
|
|
6278
|
+
Ugrave: '\u00D9',
|
|
6279
|
+
Uacute: '\u00DA',
|
|
6280
|
+
Ucirc: '\u00DB',
|
|
6281
|
+
Uuml: '\u00DC',
|
|
6282
|
+
Yacute: '\u00DD',
|
|
6283
|
+
THORN: '\u00DE',
|
|
6284
|
+
szlig: '\u00DF',
|
|
6285
|
+
agrave: '\u00E0',
|
|
6286
|
+
aacute: '\u00E1',
|
|
6287
|
+
acirc: '\u00E2',
|
|
6288
|
+
atilde: '\u00E3',
|
|
6289
|
+
auml: '\u00E4',
|
|
6290
|
+
aring: '\u00E5',
|
|
6291
|
+
aelig: '\u00E6',
|
|
6292
|
+
ccedil: '\u00E7',
|
|
6293
|
+
egrave: '\u00E8',
|
|
6294
|
+
eacute: '\u00E9',
|
|
6295
|
+
ecirc: '\u00EA',
|
|
6296
|
+
euml: '\u00EB',
|
|
6297
|
+
igrave: '\u00EC',
|
|
6298
|
+
iacute: '\u00ED',
|
|
6299
|
+
icirc: '\u00EE',
|
|
6300
|
+
iuml: '\u00EF',
|
|
6301
|
+
eth: '\u00F0',
|
|
6302
|
+
ntilde: '\u00F1',
|
|
6303
|
+
ograve: '\u00F2',
|
|
6304
|
+
oacute: '\u00F3',
|
|
6305
|
+
ocirc: '\u00F4',
|
|
6306
|
+
otilde: '\u00F5',
|
|
6307
|
+
ouml: '\u00F6',
|
|
6308
|
+
divide: '\u00F7',
|
|
6309
|
+
oslash: '\u00F8',
|
|
6310
|
+
ugrave: '\u00F9',
|
|
6311
|
+
uacute: '\u00FA',
|
|
6312
|
+
ucirc: '\u00FB',
|
|
6313
|
+
uuml: '\u00FC',
|
|
6314
|
+
yacute: '\u00FD',
|
|
6315
|
+
thorn: '\u00FE',
|
|
6316
|
+
yuml: '\u00FF',
|
|
6317
|
+
OElig: '\u0152',
|
|
6318
|
+
oelig: '\u0153',
|
|
6319
|
+
Scaron: '\u0160',
|
|
6320
|
+
scaron: '\u0161',
|
|
6321
|
+
Yuml: '\u0178',
|
|
6322
|
+
fnof: '\u0192',
|
|
6323
|
+
circ: '\u02C6',
|
|
6324
|
+
tilde: '\u02DC',
|
|
6325
|
+
Alpha: '\u0391',
|
|
6326
|
+
Beta: '\u0392',
|
|
6327
|
+
Gamma: '\u0393',
|
|
6328
|
+
Delta: '\u0394',
|
|
6329
|
+
Epsilon: '\u0395',
|
|
6330
|
+
Zeta: '\u0396',
|
|
6331
|
+
Eta: '\u0397',
|
|
6332
|
+
Theta: '\u0398',
|
|
6333
|
+
Iota: '\u0399',
|
|
6334
|
+
Kappa: '\u039A',
|
|
6335
|
+
Lambda: '\u039B',
|
|
6336
|
+
Mu: '\u039C',
|
|
6337
|
+
Nu: '\u039D',
|
|
6338
|
+
Xi: '\u039E',
|
|
6339
|
+
Omicron: '\u039F',
|
|
6340
|
+
Pi: '\u03A0',
|
|
6341
|
+
Rho: '\u03A1',
|
|
6342
|
+
Sigma: '\u03A3',
|
|
6343
|
+
Tau: '\u03A4',
|
|
6344
|
+
Upsilon: '\u03A5',
|
|
6345
|
+
Phi: '\u03A6',
|
|
6346
|
+
Chi: '\u03A7',
|
|
6347
|
+
Psi: '\u03A8',
|
|
6348
|
+
Omega: '\u03A9',
|
|
6349
|
+
alpha: '\u03B1',
|
|
6350
|
+
beta: '\u03B2',
|
|
6351
|
+
gamma: '\u03B3',
|
|
6352
|
+
delta: '\u03B4',
|
|
6353
|
+
epsilon: '\u03B5',
|
|
6354
|
+
zeta: '\u03B6',
|
|
6355
|
+
eta: '\u03B7',
|
|
6356
|
+
theta: '\u03B8',
|
|
6357
|
+
iota: '\u03B9',
|
|
6358
|
+
kappa: '\u03BA',
|
|
6359
|
+
lambda: '\u03BB',
|
|
6360
|
+
mu: '\u03BC',
|
|
6361
|
+
nu: '\u03BD',
|
|
6362
|
+
xi: '\u03BE',
|
|
6363
|
+
omicron: '\u03BF',
|
|
6364
|
+
pi: '\u03C0',
|
|
6365
|
+
rho: '\u03C1',
|
|
6366
|
+
sigmaf: '\u03C2',
|
|
6367
|
+
sigma: '\u03C3',
|
|
6368
|
+
tau: '\u03C4',
|
|
6369
|
+
upsilon: '\u03C5',
|
|
6370
|
+
phi: '\u03C6',
|
|
6371
|
+
chi: '\u03C7',
|
|
6372
|
+
psi: '\u03C8',
|
|
6373
|
+
omega: '\u03C9',
|
|
6374
|
+
thetasym: '\u03D1',
|
|
6375
|
+
upsih: '\u03D2',
|
|
6376
|
+
piv: '\u03D6',
|
|
6377
|
+
ensp: '\u2002',
|
|
6378
|
+
emsp: '\u2003',
|
|
6379
|
+
thinsp: '\u2009',
|
|
6380
|
+
zwnj: '\u200C',
|
|
6381
|
+
zwj: '\u200D',
|
|
6382
|
+
lrm: '\u200E',
|
|
6383
|
+
rlm: '\u200F',
|
|
6384
|
+
ndash: '\u2013',
|
|
6385
|
+
mdash: '\u2014',
|
|
6386
|
+
lsquo: '\u2018',
|
|
6387
|
+
rsquo: '\u2019',
|
|
6388
|
+
sbquo: '\u201A',
|
|
6389
|
+
ldquo: '\u201C',
|
|
6390
|
+
rdquo: '\u201D',
|
|
6391
|
+
bdquo: '\u201E',
|
|
6392
|
+
dagger: '\u2020',
|
|
6393
|
+
Dagger: '\u2021',
|
|
6394
|
+
bull: '\u2022',
|
|
6395
|
+
hellip: '\u2026',
|
|
6396
|
+
permil: '\u2030',
|
|
6397
|
+
prime: '\u2032',
|
|
6398
|
+
Prime: '\u2033',
|
|
6399
|
+
lsaquo: '\u2039',
|
|
6400
|
+
rsaquo: '\u203A',
|
|
6401
|
+
oline: '\u203E',
|
|
6402
|
+
frasl: '\u2044',
|
|
6403
|
+
euro: '\u20AC',
|
|
6404
|
+
image: '\u2111',
|
|
6405
|
+
weierp: '\u2118',
|
|
6406
|
+
real: '\u211C',
|
|
6407
|
+
trade: '\u2122',
|
|
6408
|
+
alefsym: '\u2135',
|
|
6409
|
+
larr: '\u2190',
|
|
6410
|
+
uarr: '\u2191',
|
|
6411
|
+
rarr: '\u2192',
|
|
6412
|
+
darr: '\u2193',
|
|
6413
|
+
harr: '\u2194',
|
|
6414
|
+
crarr: '\u21B5',
|
|
6415
|
+
lArr: '\u21D0',
|
|
6416
|
+
uArr: '\u21D1',
|
|
6417
|
+
rArr: '\u21D2',
|
|
6418
|
+
dArr: '\u21D3',
|
|
6419
|
+
hArr: '\u21D4',
|
|
6420
|
+
forall: '\u2200',
|
|
6421
|
+
part: '\u2202',
|
|
6422
|
+
exist: '\u2203',
|
|
6423
|
+
empty: '\u2205',
|
|
6424
|
+
nabla: '\u2207',
|
|
6425
|
+
isin: '\u2208',
|
|
6426
|
+
notin: '\u2209',
|
|
6427
|
+
ni: '\u220B',
|
|
6428
|
+
prod: '\u220F',
|
|
6429
|
+
sum: '\u2211',
|
|
6430
|
+
minus: '\u2212',
|
|
6431
|
+
lowast: '\u2217',
|
|
6432
|
+
radic: '\u221A',
|
|
6433
|
+
prop: '\u221D',
|
|
6434
|
+
infin: '\u221E',
|
|
6435
|
+
ang: '\u2220',
|
|
6436
|
+
and: '\u2227',
|
|
6437
|
+
or: '\u2228',
|
|
6438
|
+
cap: '\u2229',
|
|
6439
|
+
cup: '\u222A',
|
|
6440
|
+
'int': '\u222B',
|
|
6441
|
+
there4: '\u2234',
|
|
6442
|
+
sim: '\u223C',
|
|
6443
|
+
cong: '\u2245',
|
|
6444
|
+
asymp: '\u2248',
|
|
6445
|
+
ne: '\u2260',
|
|
6446
|
+
equiv: '\u2261',
|
|
6447
|
+
le: '\u2264',
|
|
6448
|
+
ge: '\u2265',
|
|
6449
|
+
sub: '\u2282',
|
|
6450
|
+
sup: '\u2283',
|
|
6451
|
+
nsub: '\u2284',
|
|
6452
|
+
sube: '\u2286',
|
|
6453
|
+
supe: '\u2287',
|
|
6454
|
+
oplus: '\u2295',
|
|
6455
|
+
otimes: '\u2297',
|
|
6456
|
+
perp: '\u22A5',
|
|
6457
|
+
sdot: '\u22C5',
|
|
6458
|
+
lceil: '\u2308',
|
|
6459
|
+
rceil: '\u2309',
|
|
6460
|
+
lfloor: '\u230A',
|
|
6461
|
+
rfloor: '\u230B',
|
|
6462
|
+
lang: '\u2329',
|
|
6463
|
+
rang: '\u232A',
|
|
6464
|
+
loz: '\u25CA',
|
|
6465
|
+
spades: '\u2660',
|
|
6466
|
+
clubs: '\u2663',
|
|
6467
|
+
hearts: '\u2665',
|
|
6468
|
+
diams: '\u2666'
|
|
6469
|
+
};
|
|
6470
|
+
|
|
6471
|
+
const hexNumber = /^[\da-fA-F]+$/;
|
|
6472
|
+
const decimalNumber = /^\d+$/;
|
|
6473
|
+
|
|
6474
|
+
// The map to `acorn-jsx` tokens from `acorn` namespace objects.
|
|
6475
|
+
const acornJsxMap = new WeakMap();
|
|
6476
|
+
|
|
6477
|
+
// Get the original tokens for the given `acorn` namespace object.
|
|
6478
|
+
function getJsxTokens(acorn) {
|
|
6479
|
+
acorn = acorn.Parser.acorn || acorn;
|
|
6480
|
+
let acornJsx = acornJsxMap.get(acorn);
|
|
6481
|
+
if (!acornJsx) {
|
|
6482
|
+
const tt = acorn.tokTypes;
|
|
6483
|
+
const TokContext = acorn.TokContext;
|
|
6484
|
+
const TokenType = acorn.TokenType;
|
|
6485
|
+
const tc_oTag = new TokContext('<tag', false);
|
|
6486
|
+
const tc_cTag = new TokContext('</tag', false);
|
|
6487
|
+
const tc_expr = new TokContext('<tag>...</tag>', true, true);
|
|
6488
|
+
const tokContexts = {
|
|
6489
|
+
tc_oTag: tc_oTag,
|
|
6490
|
+
tc_cTag: tc_cTag,
|
|
6491
|
+
tc_expr: tc_expr
|
|
6492
|
+
};
|
|
6493
|
+
const tokTypes = {
|
|
6494
|
+
jsxName: new TokenType('jsxName'),
|
|
6495
|
+
jsxText: new TokenType('jsxText', {beforeExpr: true}),
|
|
6496
|
+
jsxTagStart: new TokenType('jsxTagStart', {startsExpr: true}),
|
|
6497
|
+
jsxTagEnd: new TokenType('jsxTagEnd')
|
|
6498
|
+
};
|
|
6499
|
+
|
|
6500
|
+
tokTypes.jsxTagStart.updateContext = function() {
|
|
6501
|
+
this.context.push(tc_expr); // treat as beginning of JSX expression
|
|
6502
|
+
this.context.push(tc_oTag); // start opening tag context
|
|
6503
|
+
this.exprAllowed = false;
|
|
6504
|
+
};
|
|
6505
|
+
tokTypes.jsxTagEnd.updateContext = function(prevType) {
|
|
6506
|
+
let out = this.context.pop();
|
|
6507
|
+
if (out === tc_oTag && prevType === tt.slash || out === tc_cTag) {
|
|
6508
|
+
this.context.pop();
|
|
6509
|
+
this.exprAllowed = this.curContext() === tc_expr;
|
|
6510
|
+
} else {
|
|
6511
|
+
this.exprAllowed = true;
|
|
6512
|
+
}
|
|
6513
|
+
};
|
|
6514
|
+
|
|
6515
|
+
acornJsx = { tokContexts: tokContexts, tokTypes: tokTypes };
|
|
6516
|
+
acornJsxMap.set(acorn, acornJsx);
|
|
6517
|
+
}
|
|
6220
6518
|
|
|
6221
|
-
|
|
6222
|
-
|
|
6519
|
+
return acornJsx;
|
|
6520
|
+
}
|
|
6521
|
+
|
|
6522
|
+
// Transforms JSX element name to string.
|
|
6523
|
+
|
|
6524
|
+
function getQualifiedJSXName(object) {
|
|
6525
|
+
if (!object)
|
|
6526
|
+
return object;
|
|
6527
|
+
|
|
6528
|
+
if (object.type === 'JSXIdentifier')
|
|
6529
|
+
return object.name;
|
|
6530
|
+
|
|
6531
|
+
if (object.type === 'JSXNamespacedName')
|
|
6532
|
+
return object.namespace.name + ':' + object.name.name;
|
|
6533
|
+
|
|
6534
|
+
if (object.type === 'JSXMemberExpression')
|
|
6535
|
+
return getQualifiedJSXName(object.object) + '.' +
|
|
6536
|
+
getQualifiedJSXName(object.property);
|
|
6537
|
+
}
|
|
6538
|
+
|
|
6539
|
+
function acornJsx(options) {
|
|
6540
|
+
options = options || {};
|
|
6541
|
+
return function(Parser) {
|
|
6542
|
+
return plugin({
|
|
6543
|
+
allowNamespaces: options.allowNamespaces !== false,
|
|
6544
|
+
allowNamespacedObjects: !!options.allowNamespacedObjects
|
|
6545
|
+
}, Parser);
|
|
6546
|
+
};
|
|
6547
|
+
}
|
|
6548
|
+
// This is `tokTypes` of the peer dep.
|
|
6549
|
+
// This can be different instances from the actual `tokTypes` this plugin uses.
|
|
6550
|
+
// tokTypes property removed for bundle
|
|
6551
|
+
|
|
6552
|
+
function plugin(options, Parser) {
|
|
6553
|
+
const acorn = Parser.acorn || { tokTypes: tt, TokContext, TokenType, isNewLine, isIdentifierStart, isIdentifierChar, tokContexts };
|
|
6554
|
+
const acornJsx = getJsxTokens(acorn);
|
|
6555
|
+
const tt = acorn.tokTypes;
|
|
6556
|
+
const tok = acornJsx.tokTypes;
|
|
6557
|
+
const tokContexts = acorn.tokContexts;
|
|
6558
|
+
const tc_oTag = acornJsx.tokContexts.tc_oTag;
|
|
6559
|
+
const tc_cTag = acornJsx.tokContexts.tc_cTag;
|
|
6560
|
+
const tc_expr = acornJsx.tokContexts.tc_expr;
|
|
6561
|
+
const isNewLine = acorn.isNewLine;
|
|
6562
|
+
const isIdentifierStart = acorn.isIdentifierStart;
|
|
6563
|
+
const isIdentifierChar = acorn.isIdentifierChar;
|
|
6564
|
+
|
|
6565
|
+
return class extends Parser {
|
|
6566
|
+
// Expose actual `tokTypes` and `tokContexts` to other plugins.
|
|
6567
|
+
static get acornJsx() {
|
|
6568
|
+
return acornJsx;
|
|
6569
|
+
}
|
|
6570
|
+
|
|
6571
|
+
// Reads inline JSX contents token.
|
|
6572
|
+
jsx_readToken() {
|
|
6573
|
+
let out = '', chunkStart = this.pos;
|
|
6574
|
+
for (;;) {
|
|
6575
|
+
if (this.pos >= this.input.length)
|
|
6576
|
+
this.raise(this.start, 'Unterminated JSX contents');
|
|
6577
|
+
let ch = this.input.charCodeAt(this.pos);
|
|
6578
|
+
|
|
6579
|
+
switch (ch) {
|
|
6580
|
+
case 60: // '<'
|
|
6581
|
+
case 123: // '{'
|
|
6582
|
+
if (this.pos === this.start) {
|
|
6583
|
+
if (ch === 60 && this.exprAllowed) {
|
|
6584
|
+
++this.pos;
|
|
6585
|
+
return this.finishToken(tok.jsxTagStart);
|
|
6586
|
+
}
|
|
6587
|
+
return this.getTokenFromCode(ch);
|
|
6588
|
+
}
|
|
6589
|
+
out += this.input.slice(chunkStart, this.pos);
|
|
6590
|
+
return this.finishToken(tok.jsxText, out);
|
|
6591
|
+
|
|
6592
|
+
case 38: // '&'
|
|
6593
|
+
out += this.input.slice(chunkStart, this.pos);
|
|
6594
|
+
out += this.jsx_readEntity();
|
|
6595
|
+
chunkStart = this.pos;
|
|
6596
|
+
break;
|
|
6597
|
+
|
|
6598
|
+
case 62: // '>'
|
|
6599
|
+
case 125: // '}'
|
|
6600
|
+
this.raise(
|
|
6601
|
+
this.pos,
|
|
6602
|
+
"Unexpected token `" + this.input[this.pos] + "`. Did you mean `" +
|
|
6603
|
+
(ch === 62 ? ">" : "}") + "` or " + "`{\"" + this.input[this.pos] + "\"}" + "`?"
|
|
6604
|
+
);
|
|
6605
|
+
|
|
6606
|
+
default:
|
|
6607
|
+
if (isNewLine(ch)) {
|
|
6608
|
+
out += this.input.slice(chunkStart, this.pos);
|
|
6609
|
+
out += this.jsx_readNewLine(true);
|
|
6610
|
+
chunkStart = this.pos;
|
|
6611
|
+
} else {
|
|
6612
|
+
++this.pos;
|
|
6613
|
+
}
|
|
6614
|
+
}
|
|
6615
|
+
}
|
|
6616
|
+
}
|
|
6617
|
+
|
|
6618
|
+
jsx_readNewLine(normalizeCRLF) {
|
|
6619
|
+
let ch = this.input.charCodeAt(this.pos);
|
|
6620
|
+
let out;
|
|
6621
|
+
++this.pos;
|
|
6622
|
+
if (ch === 13 && this.input.charCodeAt(this.pos) === 10) {
|
|
6623
|
+
++this.pos;
|
|
6624
|
+
out = normalizeCRLF ? '\n' : '\r\n';
|
|
6625
|
+
} else {
|
|
6626
|
+
out = String.fromCharCode(ch);
|
|
6627
|
+
}
|
|
6628
|
+
if (this.options.locations) {
|
|
6629
|
+
++this.curLine;
|
|
6630
|
+
this.lineStart = this.pos;
|
|
6631
|
+
}
|
|
6632
|
+
|
|
6633
|
+
return out;
|
|
6634
|
+
}
|
|
6635
|
+
|
|
6636
|
+
jsx_readString(quote) {
|
|
6637
|
+
let out = '', chunkStart = ++this.pos;
|
|
6638
|
+
for (;;) {
|
|
6639
|
+
if (this.pos >= this.input.length)
|
|
6640
|
+
this.raise(this.start, 'Unterminated string constant');
|
|
6641
|
+
let ch = this.input.charCodeAt(this.pos);
|
|
6642
|
+
if (ch === quote) break;
|
|
6643
|
+
if (ch === 38) { // '&'
|
|
6644
|
+
out += this.input.slice(chunkStart, this.pos);
|
|
6645
|
+
out += this.jsx_readEntity();
|
|
6646
|
+
chunkStart = this.pos;
|
|
6647
|
+
} else if (isNewLine(ch)) {
|
|
6648
|
+
out += this.input.slice(chunkStart, this.pos);
|
|
6649
|
+
out += this.jsx_readNewLine(false);
|
|
6650
|
+
chunkStart = this.pos;
|
|
6651
|
+
} else {
|
|
6652
|
+
++this.pos;
|
|
6653
|
+
}
|
|
6654
|
+
}
|
|
6655
|
+
out += this.input.slice(chunkStart, this.pos++);
|
|
6656
|
+
return this.finishToken(tt.string, out);
|
|
6657
|
+
}
|
|
6658
|
+
|
|
6659
|
+
jsx_readEntity() {
|
|
6660
|
+
let str = '', count = 0, entity;
|
|
6661
|
+
let ch = this.input[this.pos];
|
|
6662
|
+
if (ch !== '&')
|
|
6663
|
+
this.raise(this.pos, 'Entity must start with an ampersand');
|
|
6664
|
+
let startPos = ++this.pos;
|
|
6665
|
+
while (this.pos < this.input.length && count++ < 10) {
|
|
6666
|
+
ch = this.input[this.pos++];
|
|
6667
|
+
if (ch === ';') {
|
|
6668
|
+
if (str[0] === '#') {
|
|
6669
|
+
if (str[1] === 'x') {
|
|
6670
|
+
str = str.substr(2);
|
|
6671
|
+
if (hexNumber.test(str))
|
|
6672
|
+
entity = String.fromCharCode(parseInt(str, 16));
|
|
6673
|
+
} else {
|
|
6674
|
+
str = str.substr(1);
|
|
6675
|
+
if (decimalNumber.test(str))
|
|
6676
|
+
entity = String.fromCharCode(parseInt(str, 10));
|
|
6677
|
+
}
|
|
6678
|
+
} else {
|
|
6679
|
+
entity = XHTMLEntities[str];
|
|
6680
|
+
}
|
|
6681
|
+
break;
|
|
6682
|
+
}
|
|
6683
|
+
str += ch;
|
|
6684
|
+
}
|
|
6685
|
+
if (!entity) {
|
|
6686
|
+
this.pos = startPos;
|
|
6687
|
+
return '&';
|
|
6688
|
+
}
|
|
6689
|
+
return entity;
|
|
6690
|
+
}
|
|
6691
|
+
|
|
6692
|
+
// Read a JSX identifier (valid tag or attribute name).
|
|
6693
|
+
//
|
|
6694
|
+
// Optimized version since JSX identifiers can't contain
|
|
6695
|
+
// escape characters and so can be read as single slice.
|
|
6696
|
+
// Also assumes that first character was already checked
|
|
6697
|
+
// by isIdentifierStart in readToken.
|
|
6698
|
+
|
|
6699
|
+
jsx_readWord() {
|
|
6700
|
+
let ch, start = this.pos;
|
|
6701
|
+
do {
|
|
6702
|
+
ch = this.input.charCodeAt(++this.pos);
|
|
6703
|
+
} while (isIdentifierChar(ch) || ch === 45); // '-'
|
|
6704
|
+
return this.finishToken(tok.jsxName, this.input.slice(start, this.pos));
|
|
6705
|
+
}
|
|
6706
|
+
|
|
6707
|
+
// Parse next token as JSX identifier
|
|
6708
|
+
|
|
6709
|
+
jsx_parseIdentifier() {
|
|
6710
|
+
let node = this.startNode();
|
|
6711
|
+
if (this.type === tok.jsxName)
|
|
6712
|
+
node.name = this.value;
|
|
6713
|
+
else if (this.type.keyword)
|
|
6714
|
+
node.name = this.type.keyword;
|
|
6715
|
+
else
|
|
6716
|
+
this.unexpected();
|
|
6717
|
+
this.next();
|
|
6718
|
+
return this.finishNode(node, 'JSXIdentifier');
|
|
6719
|
+
}
|
|
6720
|
+
|
|
6721
|
+
// Parse namespaced identifier.
|
|
6722
|
+
|
|
6723
|
+
jsx_parseNamespacedName() {
|
|
6724
|
+
let startPos = this.start, startLoc = this.startLoc;
|
|
6725
|
+
let name = this.jsx_parseIdentifier();
|
|
6726
|
+
if (!options.allowNamespaces || !this.eat(tt.colon)) return name;
|
|
6727
|
+
var node = this.startNodeAt(startPos, startLoc);
|
|
6728
|
+
node.namespace = name;
|
|
6729
|
+
node.name = this.jsx_parseIdentifier();
|
|
6730
|
+
return this.finishNode(node, 'JSXNamespacedName');
|
|
6731
|
+
}
|
|
6732
|
+
|
|
6733
|
+
// Parses element name in any form - namespaced, member
|
|
6734
|
+
// or single identifier.
|
|
6735
|
+
|
|
6736
|
+
jsx_parseElementName() {
|
|
6737
|
+
if (this.type === tok.jsxTagEnd) return '';
|
|
6738
|
+
let startPos = this.start, startLoc = this.startLoc;
|
|
6739
|
+
let node = this.jsx_parseNamespacedName();
|
|
6740
|
+
if (this.type === tt.dot && node.type === 'JSXNamespacedName' && !options.allowNamespacedObjects) {
|
|
6741
|
+
this.unexpected();
|
|
6742
|
+
}
|
|
6743
|
+
while (this.eat(tt.dot)) {
|
|
6744
|
+
let newNode = this.startNodeAt(startPos, startLoc);
|
|
6745
|
+
newNode.object = node;
|
|
6746
|
+
newNode.property = this.jsx_parseIdentifier();
|
|
6747
|
+
node = this.finishNode(newNode, 'JSXMemberExpression');
|
|
6748
|
+
}
|
|
6749
|
+
return node;
|
|
6750
|
+
}
|
|
6751
|
+
|
|
6752
|
+
// Parses any type of JSX attribute value.
|
|
6753
|
+
|
|
6754
|
+
jsx_parseAttributeValue() {
|
|
6755
|
+
switch (this.type) {
|
|
6756
|
+
case tt.braceL:
|
|
6757
|
+
let node = this.jsx_parseExpressionContainer();
|
|
6758
|
+
if (node.expression.type === 'JSXEmptyExpression')
|
|
6759
|
+
this.raise(node.start, 'JSX attributes must only be assigned a non-empty expression');
|
|
6760
|
+
return node;
|
|
6761
|
+
|
|
6762
|
+
case tok.jsxTagStart:
|
|
6763
|
+
case tt.string:
|
|
6764
|
+
return this.parseExprAtom();
|
|
6765
|
+
|
|
6766
|
+
default:
|
|
6767
|
+
this.raise(this.start, 'JSX value should be either an expression or a quoted JSX text');
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
|
|
6771
|
+
// JSXEmptyExpression is unique type since it doesn't actually parse anything,
|
|
6772
|
+
// and so it should start at the end of last read token (left brace) and finish
|
|
6773
|
+
// at the beginning of the next one (right brace).
|
|
6774
|
+
|
|
6775
|
+
jsx_parseEmptyExpression() {
|
|
6776
|
+
let node = this.startNodeAt(this.lastTokEnd, this.lastTokEndLoc);
|
|
6777
|
+
return this.finishNodeAt(node, 'JSXEmptyExpression', this.start, this.startLoc);
|
|
6778
|
+
}
|
|
6779
|
+
|
|
6780
|
+
// Parses JSX expression enclosed into curly brackets.
|
|
6781
|
+
|
|
6782
|
+
jsx_parseExpressionContainer() {
|
|
6783
|
+
let node = this.startNode();
|
|
6784
|
+
this.next();
|
|
6785
|
+
node.expression = this.type === tt.braceR
|
|
6786
|
+
? this.jsx_parseEmptyExpression()
|
|
6787
|
+
: this.parseExpression();
|
|
6788
|
+
this.expect(tt.braceR);
|
|
6789
|
+
return this.finishNode(node, 'JSXExpressionContainer');
|
|
6790
|
+
}
|
|
6791
|
+
|
|
6792
|
+
// Parses following JSX attribute name-value pair.
|
|
6793
|
+
|
|
6794
|
+
jsx_parseAttribute() {
|
|
6795
|
+
let node = this.startNode();
|
|
6796
|
+
if (this.eat(tt.braceL)) {
|
|
6797
|
+
this.expect(tt.ellipsis);
|
|
6798
|
+
node.argument = this.parseMaybeAssign();
|
|
6799
|
+
this.expect(tt.braceR);
|
|
6800
|
+
return this.finishNode(node, 'JSXSpreadAttribute');
|
|
6801
|
+
}
|
|
6802
|
+
node.name = this.jsx_parseNamespacedName();
|
|
6803
|
+
node.value = this.eat(tt.eq) ? this.jsx_parseAttributeValue() : null;
|
|
6804
|
+
return this.finishNode(node, 'JSXAttribute');
|
|
6805
|
+
}
|
|
6806
|
+
|
|
6807
|
+
// Parses JSX opening tag starting after '<'.
|
|
6808
|
+
|
|
6809
|
+
jsx_parseOpeningElementAt(startPos, startLoc) {
|
|
6810
|
+
let node = this.startNodeAt(startPos, startLoc);
|
|
6811
|
+
node.attributes = [];
|
|
6812
|
+
let nodeName = this.jsx_parseElementName();
|
|
6813
|
+
if (nodeName) node.name = nodeName;
|
|
6814
|
+
while (this.type !== tt.slash && this.type !== tok.jsxTagEnd)
|
|
6815
|
+
node.attributes.push(this.jsx_parseAttribute());
|
|
6816
|
+
node.selfClosing = this.eat(tt.slash);
|
|
6817
|
+
this.expect(tok.jsxTagEnd);
|
|
6818
|
+
return this.finishNode(node, nodeName ? 'JSXOpeningElement' : 'JSXOpeningFragment');
|
|
6819
|
+
}
|
|
6820
|
+
|
|
6821
|
+
// Parses JSX closing tag starting after '</'.
|
|
6822
|
+
|
|
6823
|
+
jsx_parseClosingElementAt(startPos, startLoc) {
|
|
6824
|
+
let node = this.startNodeAt(startPos, startLoc);
|
|
6825
|
+
let nodeName = this.jsx_parseElementName();
|
|
6826
|
+
if (nodeName) node.name = nodeName;
|
|
6827
|
+
this.expect(tok.jsxTagEnd);
|
|
6828
|
+
return this.finishNode(node, nodeName ? 'JSXClosingElement' : 'JSXClosingFragment');
|
|
6829
|
+
}
|
|
6830
|
+
|
|
6831
|
+
// Parses entire JSX element, including it's opening tag
|
|
6832
|
+
// (starting after '<'), attributes, contents and closing tag.
|
|
6833
|
+
|
|
6834
|
+
jsx_parseElementAt(startPos, startLoc) {
|
|
6835
|
+
let node = this.startNodeAt(startPos, startLoc);
|
|
6836
|
+
let children = [];
|
|
6837
|
+
let openingElement = this.jsx_parseOpeningElementAt(startPos, startLoc);
|
|
6838
|
+
let closingElement = null;
|
|
6839
|
+
|
|
6840
|
+
if (!openingElement.selfClosing) {
|
|
6841
|
+
contents: for (;;) {
|
|
6842
|
+
switch (this.type) {
|
|
6843
|
+
case tok.jsxTagStart:
|
|
6844
|
+
startPos = this.start; startLoc = this.startLoc;
|
|
6845
|
+
this.next();
|
|
6846
|
+
if (this.eat(tt.slash)) {
|
|
6847
|
+
closingElement = this.jsx_parseClosingElementAt(startPos, startLoc);
|
|
6848
|
+
break contents;
|
|
6849
|
+
}
|
|
6850
|
+
children.push(this.jsx_parseElementAt(startPos, startLoc));
|
|
6851
|
+
break;
|
|
6852
|
+
|
|
6853
|
+
case tok.jsxText:
|
|
6854
|
+
children.push(this.parseExprAtom());
|
|
6855
|
+
break;
|
|
6856
|
+
|
|
6857
|
+
case tt.braceL:
|
|
6858
|
+
children.push(this.jsx_parseExpressionContainer());
|
|
6859
|
+
break;
|
|
6860
|
+
|
|
6861
|
+
default:
|
|
6862
|
+
this.unexpected();
|
|
6863
|
+
}
|
|
6864
|
+
}
|
|
6865
|
+
if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {
|
|
6866
|
+
this.raise(
|
|
6867
|
+
closingElement.start,
|
|
6868
|
+
'Expected corresponding JSX closing tag for <' + getQualifiedJSXName(openingElement.name) + '>');
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
let fragmentOrElement = openingElement.name ? 'Element' : 'Fragment';
|
|
6872
|
+
|
|
6873
|
+
node['opening' + fragmentOrElement] = openingElement;
|
|
6874
|
+
node['closing' + fragmentOrElement] = closingElement;
|
|
6875
|
+
node.children = children;
|
|
6876
|
+
if (this.type === tt.relational && this.value === "<") {
|
|
6877
|
+
this.raise(this.start, "Adjacent JSX elements must be wrapped in an enclosing tag");
|
|
6878
|
+
}
|
|
6879
|
+
return this.finishNode(node, 'JSX' + fragmentOrElement);
|
|
6880
|
+
}
|
|
6881
|
+
|
|
6882
|
+
// Parse JSX text
|
|
6883
|
+
|
|
6884
|
+
jsx_parseText() {
|
|
6885
|
+
let node = this.parseLiteral(this.value);
|
|
6886
|
+
node.type = "JSXText";
|
|
6887
|
+
return node;
|
|
6888
|
+
}
|
|
6889
|
+
|
|
6890
|
+
// Parses entire JSX element from current position.
|
|
6891
|
+
|
|
6892
|
+
jsx_parseElement() {
|
|
6893
|
+
let startPos = this.start, startLoc = this.startLoc;
|
|
6894
|
+
this.next();
|
|
6895
|
+
return this.jsx_parseElementAt(startPos, startLoc);
|
|
6896
|
+
}
|
|
6897
|
+
|
|
6898
|
+
parseExprAtom(refShortHandDefaultPos) {
|
|
6899
|
+
if (this.type === tok.jsxText)
|
|
6900
|
+
return this.jsx_parseText();
|
|
6901
|
+
else if (this.type === tok.jsxTagStart)
|
|
6902
|
+
return this.jsx_parseElement();
|
|
6903
|
+
else
|
|
6904
|
+
return super.parseExprAtom(refShortHandDefaultPos);
|
|
6905
|
+
}
|
|
6906
|
+
|
|
6907
|
+
readToken(code) {
|
|
6908
|
+
let context = this.curContext();
|
|
6909
|
+
|
|
6910
|
+
if (context === tc_expr) return this.jsx_readToken();
|
|
6911
|
+
|
|
6912
|
+
if (context === tc_oTag || context === tc_cTag) {
|
|
6913
|
+
if (isIdentifierStart(code)) return this.jsx_readWord();
|
|
6914
|
+
|
|
6915
|
+
if (code == 62) {
|
|
6916
|
+
++this.pos;
|
|
6917
|
+
return this.finishToken(tok.jsxTagEnd);
|
|
6918
|
+
}
|
|
6919
|
+
|
|
6920
|
+
if ((code === 34 || code === 39) && context == tc_oTag)
|
|
6921
|
+
return this.jsx_readString(code);
|
|
6922
|
+
}
|
|
6923
|
+
|
|
6924
|
+
if (code === 60 && this.exprAllowed && this.input.charCodeAt(this.pos + 1) !== 33) {
|
|
6925
|
+
++this.pos;
|
|
6926
|
+
return this.finishToken(tok.jsxTagStart);
|
|
6927
|
+
}
|
|
6928
|
+
return super.readToken(code);
|
|
6929
|
+
}
|
|
6930
|
+
|
|
6931
|
+
updateContext(prevType) {
|
|
6932
|
+
if (this.type == tt.braceL) {
|
|
6933
|
+
var curContext = this.curContext();
|
|
6934
|
+
if (curContext == tc_oTag) this.context.push(tokContexts.b_expr);
|
|
6935
|
+
else if (curContext == tc_expr) this.context.push(tokContexts.b_tmpl);
|
|
6936
|
+
else super.updateContext(prevType);
|
|
6937
|
+
this.exprAllowed = true;
|
|
6938
|
+
} else if (this.type === tt.slash && prevType === tok.jsxTagStart) {
|
|
6939
|
+
this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore
|
|
6940
|
+
this.context.push(tc_cTag); // reconsider as closing tag context
|
|
6941
|
+
this.exprAllowed = false;
|
|
6942
|
+
} else {
|
|
6943
|
+
return super.updateContext(prevType);
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6946
|
+
};
|
|
6947
|
+
}
|
|
6948
|
+
|
|
6949
|
+
|
|
6950
|
+
// ========== Create JSX-enabled Parser ==========
|
|
6951
|
+
|
|
6952
|
+
const JSXParser = Parser.extend(acornJsx());
|
|
6953
|
+
|
|
6954
|
+
// JSX-aware parse function that replaces the original
|
|
6955
|
+
function jsxParse(input, options) {
|
|
6956
|
+
return JSXParser.parse(input, options);
|
|
6223
6957
|
}
|
|
6224
6958
|
|
|
6225
6959
|
// Runtime Environment for variable scoping and storage
|
|
@@ -6440,10 +7174,17 @@ class Interpreter {
|
|
|
6440
7174
|
this.moduleCache = new Map(); // Cache loaded modules
|
|
6441
7175
|
this.moduleExports = {}; // Track exports in current module
|
|
6442
7176
|
this.abortSignal = options.abortSignal;
|
|
7177
|
+
this.executionController = options.executionController;
|
|
6443
7178
|
}
|
|
6444
7179
|
|
|
6445
|
-
// Check if execution should be aborted
|
|
7180
|
+
// Check if execution should be aborted (sync version)
|
|
6446
7181
|
checkAbortSignal() {
|
|
7182
|
+
// Check controller first if available
|
|
7183
|
+
if (this.executionController) {
|
|
7184
|
+
this.executionController._checkAbortSync();
|
|
7185
|
+
return;
|
|
7186
|
+
}
|
|
7187
|
+
// Fall back to legacy abortSignal
|
|
6447
7188
|
if (this.abortSignal && this.abortSignal.aborted) {
|
|
6448
7189
|
const error = new Error('The operation was aborted');
|
|
6449
7190
|
error.name = 'AbortError';
|
|
@@ -6451,12 +7192,26 @@ class Interpreter {
|
|
|
6451
7192
|
}
|
|
6452
7193
|
}
|
|
6453
7194
|
|
|
7195
|
+
// Checkpoint that returns a promise only when controller is present
|
|
7196
|
+
// When no controller, returns null to signal no await needed
|
|
7197
|
+
_getCheckpointPromise(node, env) {
|
|
7198
|
+
if (this.executionController) {
|
|
7199
|
+
this.executionController._setEnv(env);
|
|
7200
|
+
return this.executionController._checkpoint(node);
|
|
7201
|
+
} else {
|
|
7202
|
+
this.checkAbortSignal();
|
|
7203
|
+
return null; // Signal that no await is needed
|
|
7204
|
+
}
|
|
7205
|
+
}
|
|
7206
|
+
|
|
6454
7207
|
// Async evaluation for async functions - handles await expressions
|
|
6455
7208
|
async evaluateAsync(node, env) {
|
|
6456
7209
|
if (!node) return undefined;
|
|
6457
7210
|
|
|
6458
|
-
//
|
|
6459
|
-
|
|
7211
|
+
// Checkpoint - yields if paused, throws if aborted
|
|
7212
|
+
// Only await when there's actually a promise (controller present)
|
|
7213
|
+
const checkpointPromise = this._getCheckpointPromise(node, env);
|
|
7214
|
+
if (checkpointPromise) await checkpointPromise;
|
|
6460
7215
|
|
|
6461
7216
|
// Handle await expressions by actually awaiting the promise
|
|
6462
7217
|
if (node.type === 'AwaitExpression') {
|
|
@@ -6693,6 +7448,9 @@ class Interpreter {
|
|
|
6693
7448
|
await this.evaluateAsync(node.init, forEnv);
|
|
6694
7449
|
}
|
|
6695
7450
|
while (!node.test || await this.evaluateAsync(node.test, forEnv)) {
|
|
7451
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
7452
|
+
const cp1 = this._getCheckpointPromise(node, forEnv);
|
|
7453
|
+
if (cp1) await cp1;
|
|
6696
7454
|
const result = await this.evaluateAsync(node.body, forEnv);
|
|
6697
7455
|
if (result instanceof BreakSignal) {
|
|
6698
7456
|
break;
|
|
@@ -6721,6 +7479,9 @@ class Interpreter {
|
|
|
6721
7479
|
const isConst = node.left.kind === 'const';
|
|
6722
7480
|
|
|
6723
7481
|
for (const value of iterable) {
|
|
7482
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
7483
|
+
const cp2 = this._getCheckpointPromise(node, forEnv);
|
|
7484
|
+
if (cp2) await cp2;
|
|
6724
7485
|
const iterEnv = forEnv.extend();
|
|
6725
7486
|
if (declarator.id.type === 'Identifier') {
|
|
6726
7487
|
iterEnv.define(declarator.id.name, value, isConst);
|
|
@@ -6754,6 +7515,9 @@ class Interpreter {
|
|
|
6754
7515
|
forEnv.define(varName, undefined);
|
|
6755
7516
|
|
|
6756
7517
|
for (const key in obj) {
|
|
7518
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
7519
|
+
const cp3 = this._getCheckpointPromise(node, forEnv);
|
|
7520
|
+
if (cp3) await cp3;
|
|
6757
7521
|
forEnv.set(varName, key);
|
|
6758
7522
|
const result = await this.evaluateAsync(node.body, forEnv);
|
|
6759
7523
|
if (result instanceof BreakSignal) {
|
|
@@ -6772,6 +7536,9 @@ class Interpreter {
|
|
|
6772
7536
|
// For WhileStatement with async body
|
|
6773
7537
|
if (node.type === 'WhileStatement') {
|
|
6774
7538
|
while (await this.evaluateAsync(node.test, env)) {
|
|
7539
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
7540
|
+
const cp4 = this._getCheckpointPromise(node, env);
|
|
7541
|
+
if (cp4) await cp4;
|
|
6775
7542
|
const result = await this.evaluateAsync(node.body, env);
|
|
6776
7543
|
if (result instanceof BreakSignal) {
|
|
6777
7544
|
break;
|
|
@@ -6789,6 +7556,9 @@ class Interpreter {
|
|
|
6789
7556
|
// For DoWhileStatement with async body
|
|
6790
7557
|
if (node.type === 'DoWhileStatement') {
|
|
6791
7558
|
do {
|
|
7559
|
+
// Checkpoint at each loop iteration (only await if controller present)
|
|
7560
|
+
const cp5 = this._getCheckpointPromise(node, env);
|
|
7561
|
+
if (cp5) await cp5;
|
|
6792
7562
|
const result = await this.evaluateAsync(node.body, env);
|
|
6793
7563
|
if (result instanceof BreakSignal) {
|
|
6794
7564
|
break;
|
|
@@ -7031,6 +7801,26 @@ class Interpreter {
|
|
|
7031
7801
|
return this.evaluateClassExpression(node, env);
|
|
7032
7802
|
}
|
|
7033
7803
|
|
|
7804
|
+
// JSX Support (async)
|
|
7805
|
+
if (node.type === 'JSXElement') {
|
|
7806
|
+
return await this.evaluateJSXElementAsync(node, env);
|
|
7807
|
+
}
|
|
7808
|
+
|
|
7809
|
+
if (node.type === 'JSXFragment') {
|
|
7810
|
+
return await this.evaluateJSXFragmentAsync(node, env);
|
|
7811
|
+
}
|
|
7812
|
+
|
|
7813
|
+
if (node.type === 'JSXExpressionContainer') {
|
|
7814
|
+
if (node.expression.type === 'JSXEmptyExpression') {
|
|
7815
|
+
return undefined;
|
|
7816
|
+
}
|
|
7817
|
+
return await this.evaluateAsync(node.expression, env);
|
|
7818
|
+
}
|
|
7819
|
+
|
|
7820
|
+
if (node.type === 'JSXText') {
|
|
7821
|
+
return this.normalizeJSXText(node.value);
|
|
7822
|
+
}
|
|
7823
|
+
|
|
7034
7824
|
// Only leaf nodes should fall through to sync evaluate
|
|
7035
7825
|
// These have no sub-expressions that could contain await
|
|
7036
7826
|
if (['Literal', 'Identifier', 'BreakStatement', 'ContinueStatement',
|
|
@@ -7205,6 +7995,22 @@ class Interpreter {
|
|
|
7205
7995
|
case 'Property':
|
|
7206
7996
|
return this.evaluateProperty(node, env);
|
|
7207
7997
|
|
|
7998
|
+
// JSX Support
|
|
7999
|
+
case 'JSXElement':
|
|
8000
|
+
return this.evaluateJSXElement(node, env);
|
|
8001
|
+
|
|
8002
|
+
case 'JSXFragment':
|
|
8003
|
+
return this.evaluateJSXFragment(node, env);
|
|
8004
|
+
|
|
8005
|
+
case 'JSXExpressionContainer':
|
|
8006
|
+
if (node.expression.type === 'JSXEmptyExpression') {
|
|
8007
|
+
return undefined;
|
|
8008
|
+
}
|
|
8009
|
+
return this.evaluate(node.expression, env);
|
|
8010
|
+
|
|
8011
|
+
case 'JSXText':
|
|
8012
|
+
return this.normalizeJSXText(node.value);
|
|
8013
|
+
|
|
7208
8014
|
default:
|
|
7209
8015
|
throw new Error(`Unknown node type: ${node.type}`);
|
|
7210
8016
|
}
|
|
@@ -7592,6 +8398,9 @@ class Interpreter {
|
|
|
7592
8398
|
const metadata = func.__metadata || func;
|
|
7593
8399
|
const funcEnv = new Environment(metadata.closure);
|
|
7594
8400
|
|
|
8401
|
+
// Get function name for call stack tracking
|
|
8402
|
+
const funcName = metadata.name || func.name || 'anonymous';
|
|
8403
|
+
|
|
7595
8404
|
// Bind 'this' if provided (for method calls)
|
|
7596
8405
|
if (thisContext !== undefined) {
|
|
7597
8406
|
funcEnv.define('this', thisContext);
|
|
@@ -7627,18 +8436,54 @@ class Interpreter {
|
|
|
7627
8436
|
// Execute function body
|
|
7628
8437
|
// If async, use async evaluation and return a promise
|
|
7629
8438
|
if (metadata.async) {
|
|
8439
|
+
// Track call stack for async functions
|
|
8440
|
+
if (this.executionController) {
|
|
8441
|
+
this.executionController._pushCall(funcName);
|
|
8442
|
+
}
|
|
7630
8443
|
return (async () => {
|
|
8444
|
+
try {
|
|
8445
|
+
if (metadata.expression) {
|
|
8446
|
+
// Arrow function with expression body
|
|
8447
|
+
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
8448
|
+
// If the result is a ThrowSignal, throw the error
|
|
8449
|
+
if (result instanceof ThrowSignal) {
|
|
8450
|
+
throw result.value;
|
|
8451
|
+
}
|
|
8452
|
+
return result;
|
|
8453
|
+
} else {
|
|
8454
|
+
// Block statement body
|
|
8455
|
+
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
8456
|
+
if (result instanceof ReturnValue) {
|
|
8457
|
+
return result.value;
|
|
8458
|
+
}
|
|
8459
|
+
// If the result is a ThrowSignal, throw the error
|
|
8460
|
+
if (result instanceof ThrowSignal) {
|
|
8461
|
+
throw result.value;
|
|
8462
|
+
}
|
|
8463
|
+
return undefined;
|
|
8464
|
+
}
|
|
8465
|
+
} finally {
|
|
8466
|
+
if (this.executionController) {
|
|
8467
|
+
this.executionController._popCall();
|
|
8468
|
+
}
|
|
8469
|
+
}
|
|
8470
|
+
})();
|
|
8471
|
+
} else {
|
|
8472
|
+
// Synchronous evaluation for non-async functions
|
|
8473
|
+
// Track call stack for sync functions
|
|
8474
|
+
if (this.executionController) {
|
|
8475
|
+
this.executionController._pushCall(funcName);
|
|
8476
|
+
}
|
|
8477
|
+
try {
|
|
7631
8478
|
if (metadata.expression) {
|
|
7632
|
-
|
|
7633
|
-
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
8479
|
+
const result = this.evaluate(metadata.body, funcEnv);
|
|
7634
8480
|
// If the result is a ThrowSignal, throw the error
|
|
7635
8481
|
if (result instanceof ThrowSignal) {
|
|
7636
8482
|
throw result.value;
|
|
7637
8483
|
}
|
|
7638
8484
|
return result;
|
|
7639
8485
|
} else {
|
|
7640
|
-
|
|
7641
|
-
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
8486
|
+
const result = this.evaluate(metadata.body, funcEnv);
|
|
7642
8487
|
if (result instanceof ReturnValue) {
|
|
7643
8488
|
return result.value;
|
|
7644
8489
|
}
|
|
@@ -7648,26 +8493,10 @@ class Interpreter {
|
|
|
7648
8493
|
}
|
|
7649
8494
|
return undefined;
|
|
7650
8495
|
}
|
|
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;
|
|
8496
|
+
} finally {
|
|
8497
|
+
if (this.executionController) {
|
|
8498
|
+
this.executionController._popCall();
|
|
7669
8499
|
}
|
|
7670
|
-
return undefined;
|
|
7671
8500
|
}
|
|
7672
8501
|
}
|
|
7673
8502
|
}
|
|
@@ -8011,7 +8840,7 @@ class Interpreter {
|
|
|
8011
8840
|
const moduleCode = typeof resolution === 'string' ? resolution : resolution.code;
|
|
8012
8841
|
|
|
8013
8842
|
// Parse and execute module in its own environment
|
|
8014
|
-
const moduleAst =
|
|
8843
|
+
const moduleAst = jsxParse(moduleCode, {
|
|
8015
8844
|
ecmaVersion: 2020,
|
|
8016
8845
|
sourceType: 'module',
|
|
8017
8846
|
locations: false
|
|
@@ -8606,6 +9435,255 @@ class Interpreter {
|
|
|
8606
9435
|
// Already handled in evaluateObjectExpression
|
|
8607
9436
|
return undefined;
|
|
8608
9437
|
}
|
|
9438
|
+
|
|
9439
|
+
// ===== JSX Support =====
|
|
9440
|
+
|
|
9441
|
+
evaluateJSXElement(node, env) {
|
|
9442
|
+
const createElement = this.getCreateElement(env);
|
|
9443
|
+
const { type, props } = this.evaluateJSXOpeningElement(node.openingElement, env);
|
|
9444
|
+
const children = this.evaluateJSXChildren(node.children, env);
|
|
9445
|
+
|
|
9446
|
+
if (children.length === 0) {
|
|
9447
|
+
return createElement(type, props);
|
|
9448
|
+
} else if (children.length === 1) {
|
|
9449
|
+
return createElement(type, props, children[0]);
|
|
9450
|
+
}
|
|
9451
|
+
return createElement(type, props, ...children);
|
|
9452
|
+
}
|
|
9453
|
+
|
|
9454
|
+
evaluateJSXFragment(node, env) {
|
|
9455
|
+
const createElement = this.getCreateElement(env);
|
|
9456
|
+
const Fragment = this.getFragment(env);
|
|
9457
|
+
const children = this.evaluateJSXChildren(node.children, env);
|
|
9458
|
+
|
|
9459
|
+
if (children.length === 0) {
|
|
9460
|
+
return createElement(Fragment, null);
|
|
9461
|
+
} else if (children.length === 1) {
|
|
9462
|
+
return createElement(Fragment, null, children[0]);
|
|
9463
|
+
}
|
|
9464
|
+
return createElement(Fragment, null, ...children);
|
|
9465
|
+
}
|
|
9466
|
+
|
|
9467
|
+
evaluateJSXOpeningElement(node, env) {
|
|
9468
|
+
const type = this.evaluateJSXElementName(node.name, env);
|
|
9469
|
+
const props = {};
|
|
9470
|
+
|
|
9471
|
+
for (const attr of node.attributes) {
|
|
9472
|
+
if (attr.type === 'JSXAttribute') {
|
|
9473
|
+
const name = attr.name.type === 'JSXIdentifier'
|
|
9474
|
+
? attr.name.name
|
|
9475
|
+
: `${attr.name.namespace.name}:${attr.name.name.name}`;
|
|
9476
|
+
const value = attr.value
|
|
9477
|
+
? this.evaluateJSXAttributeValue(attr.value, env)
|
|
9478
|
+
: true;
|
|
9479
|
+
props[name] = value;
|
|
9480
|
+
} else if (attr.type === 'JSXSpreadAttribute') {
|
|
9481
|
+
Object.assign(props, this.evaluate(attr.argument, env));
|
|
9482
|
+
}
|
|
9483
|
+
}
|
|
9484
|
+
|
|
9485
|
+
return { type, props: Object.keys(props).length > 0 ? props : null };
|
|
9486
|
+
}
|
|
9487
|
+
|
|
9488
|
+
evaluateJSXElementName(node, env) {
|
|
9489
|
+
if (node.type === 'JSXIdentifier') {
|
|
9490
|
+
const name = node.name;
|
|
9491
|
+
// Lowercase = intrinsic ('div'), Uppercase = component
|
|
9492
|
+
if (name[0] === name[0].toLowerCase()) {
|
|
9493
|
+
return name;
|
|
9494
|
+
}
|
|
9495
|
+
return env.get(name);
|
|
9496
|
+
} else if (node.type === 'JSXMemberExpression') {
|
|
9497
|
+
const object = this.evaluateJSXElementName(node.object, env);
|
|
9498
|
+
return object[node.property.name];
|
|
9499
|
+
} else if (node.type === 'JSXNamespacedName') {
|
|
9500
|
+
return `${node.namespace.name}:${node.name.name}`;
|
|
9501
|
+
}
|
|
9502
|
+
throw new Error(`Unknown JSX element name type: ${node.type}`);
|
|
9503
|
+
}
|
|
9504
|
+
|
|
9505
|
+
evaluateJSXAttributeValue(node, env) {
|
|
9506
|
+
if (node.type === 'Literal') return node.value;
|
|
9507
|
+
if (node.type === 'JSXExpressionContainer') {
|
|
9508
|
+
return this.evaluate(node.expression, env);
|
|
9509
|
+
}
|
|
9510
|
+
if (node.type === 'JSXElement') return this.evaluateJSXElement(node, env);
|
|
9511
|
+
if (node.type === 'JSXFragment') return this.evaluateJSXFragment(node, env);
|
|
9512
|
+
throw new Error(`Unknown JSX attribute value type: ${node.type}`);
|
|
9513
|
+
}
|
|
9514
|
+
|
|
9515
|
+
evaluateJSXChildren(children, env) {
|
|
9516
|
+
const result = [];
|
|
9517
|
+
for (const child of children) {
|
|
9518
|
+
if (child.type === 'JSXText') {
|
|
9519
|
+
const text = this.normalizeJSXText(child.value);
|
|
9520
|
+
if (text) result.push(text);
|
|
9521
|
+
} else if (child.type === 'JSXExpressionContainer') {
|
|
9522
|
+
if (child.expression.type !== 'JSXEmptyExpression') {
|
|
9523
|
+
const value = this.evaluate(child.expression, env);
|
|
9524
|
+
if (Array.isArray(value)) {
|
|
9525
|
+
result.push(...value);
|
|
9526
|
+
} else if (value !== null && value !== undefined && value !== false) {
|
|
9527
|
+
result.push(value);
|
|
9528
|
+
}
|
|
9529
|
+
}
|
|
9530
|
+
} else if (child.type === 'JSXElement') {
|
|
9531
|
+
result.push(this.evaluateJSXElement(child, env));
|
|
9532
|
+
} else if (child.type === 'JSXFragment') {
|
|
9533
|
+
result.push(this.evaluateJSXFragment(child, env));
|
|
9534
|
+
}
|
|
9535
|
+
}
|
|
9536
|
+
return result;
|
|
9537
|
+
}
|
|
9538
|
+
|
|
9539
|
+
normalizeJSXText(text) {
|
|
9540
|
+
// React's JSX whitespace normalization
|
|
9541
|
+
const lines = text.split('\n');
|
|
9542
|
+
const normalized = lines
|
|
9543
|
+
.map((line, i) => {
|
|
9544
|
+
let result = line;
|
|
9545
|
+
if (i === 0) result = result.trimStart();
|
|
9546
|
+
if (i === lines.length - 1) result = result.trimEnd();
|
|
9547
|
+
return result;
|
|
9548
|
+
})
|
|
9549
|
+
.filter(line => line.length > 0)
|
|
9550
|
+
.join(' ');
|
|
9551
|
+
return normalized || null;
|
|
9552
|
+
}
|
|
9553
|
+
|
|
9554
|
+
getCreateElement(env) {
|
|
9555
|
+
// Try React.createElement first
|
|
9556
|
+
try {
|
|
9557
|
+
const React = env.get('React');
|
|
9558
|
+
if (React && React.createElement) {
|
|
9559
|
+
return React.createElement.bind(React);
|
|
9560
|
+
}
|
|
9561
|
+
} catch (e) { /* not defined */ }
|
|
9562
|
+
|
|
9563
|
+
// Try standalone createElement
|
|
9564
|
+
try {
|
|
9565
|
+
return env.get('createElement');
|
|
9566
|
+
} catch (e) { /* not defined */ }
|
|
9567
|
+
|
|
9568
|
+
// Fallback: simple element factory for non-React usage
|
|
9569
|
+
return (type, props, ...children) => ({
|
|
9570
|
+
$$typeof: Symbol.for('react.element'),
|
|
9571
|
+
type,
|
|
9572
|
+
props: {
|
|
9573
|
+
...props,
|
|
9574
|
+
children: children.length === 0 ? undefined : children.length === 1 ? children[0] : children
|
|
9575
|
+
},
|
|
9576
|
+
key: props?.key ?? null,
|
|
9577
|
+
ref: props?.ref ?? null
|
|
9578
|
+
});
|
|
9579
|
+
}
|
|
9580
|
+
|
|
9581
|
+
getFragment(env) {
|
|
9582
|
+
// Try React.Fragment
|
|
9583
|
+
try {
|
|
9584
|
+
const React = env.get('React');
|
|
9585
|
+
if (React && React.Fragment) {
|
|
9586
|
+
return React.Fragment;
|
|
9587
|
+
}
|
|
9588
|
+
} catch (e) { /* not defined */ }
|
|
9589
|
+
|
|
9590
|
+
// Try standalone Fragment
|
|
9591
|
+
try {
|
|
9592
|
+
return env.get('Fragment');
|
|
9593
|
+
} catch (e) { /* not defined */ }
|
|
9594
|
+
|
|
9595
|
+
// Fallback: Symbol for fragments
|
|
9596
|
+
return Symbol.for('react.fragment');
|
|
9597
|
+
}
|
|
9598
|
+
|
|
9599
|
+
// ===== Async JSX Support =====
|
|
9600
|
+
|
|
9601
|
+
async evaluateJSXElementAsync(node, env) {
|
|
9602
|
+
const checkpointPromise = this._getCheckpointPromise(node, env);
|
|
9603
|
+
if (checkpointPromise) await checkpointPromise;
|
|
9604
|
+
|
|
9605
|
+
const createElement = this.getCreateElement(env);
|
|
9606
|
+
const { type, props } = await this.evaluateJSXOpeningElementAsync(node.openingElement, env);
|
|
9607
|
+
const children = await this.evaluateJSXChildrenAsync(node.children, env);
|
|
9608
|
+
|
|
9609
|
+
if (children.length === 0) {
|
|
9610
|
+
return createElement(type, props);
|
|
9611
|
+
} else if (children.length === 1) {
|
|
9612
|
+
return createElement(type, props, children[0]);
|
|
9613
|
+
}
|
|
9614
|
+
return createElement(type, props, ...children);
|
|
9615
|
+
}
|
|
9616
|
+
|
|
9617
|
+
async evaluateJSXFragmentAsync(node, env) {
|
|
9618
|
+
const checkpointPromise = this._getCheckpointPromise(node, env);
|
|
9619
|
+
if (checkpointPromise) await checkpointPromise;
|
|
9620
|
+
|
|
9621
|
+
const createElement = this.getCreateElement(env);
|
|
9622
|
+
const Fragment = this.getFragment(env);
|
|
9623
|
+
const children = await this.evaluateJSXChildrenAsync(node.children, env);
|
|
9624
|
+
|
|
9625
|
+
if (children.length === 0) {
|
|
9626
|
+
return createElement(Fragment, null);
|
|
9627
|
+
} else if (children.length === 1) {
|
|
9628
|
+
return createElement(Fragment, null, children[0]);
|
|
9629
|
+
}
|
|
9630
|
+
return createElement(Fragment, null, ...children);
|
|
9631
|
+
}
|
|
9632
|
+
|
|
9633
|
+
async evaluateJSXOpeningElementAsync(node, env) {
|
|
9634
|
+
const type = this.evaluateJSXElementName(node.name, env);
|
|
9635
|
+
const props = {};
|
|
9636
|
+
|
|
9637
|
+
for (const attr of node.attributes) {
|
|
9638
|
+
if (attr.type === 'JSXAttribute') {
|
|
9639
|
+
const name = attr.name.type === 'JSXIdentifier'
|
|
9640
|
+
? attr.name.name
|
|
9641
|
+
: `${attr.name.namespace.name}:${attr.name.name.name}`;
|
|
9642
|
+
const value = attr.value
|
|
9643
|
+
? await this.evaluateJSXAttributeValueAsync(attr.value, env)
|
|
9644
|
+
: true;
|
|
9645
|
+
props[name] = value;
|
|
9646
|
+
} else if (attr.type === 'JSXSpreadAttribute') {
|
|
9647
|
+
Object.assign(props, await this.evaluateAsync(attr.argument, env));
|
|
9648
|
+
}
|
|
9649
|
+
}
|
|
9650
|
+
|
|
9651
|
+
return { type, props: Object.keys(props).length > 0 ? props : null };
|
|
9652
|
+
}
|
|
9653
|
+
|
|
9654
|
+
async evaluateJSXAttributeValueAsync(node, env) {
|
|
9655
|
+
if (node.type === 'Literal') return node.value;
|
|
9656
|
+
if (node.type === 'JSXExpressionContainer') {
|
|
9657
|
+
return await this.evaluateAsync(node.expression, env);
|
|
9658
|
+
}
|
|
9659
|
+
if (node.type === 'JSXElement') return await this.evaluateJSXElementAsync(node, env);
|
|
9660
|
+
if (node.type === 'JSXFragment') return await this.evaluateJSXFragmentAsync(node, env);
|
|
9661
|
+
throw new Error(`Unknown JSX attribute value type: ${node.type}`);
|
|
9662
|
+
}
|
|
9663
|
+
|
|
9664
|
+
async evaluateJSXChildrenAsync(children, env) {
|
|
9665
|
+
const result = [];
|
|
9666
|
+
for (const child of children) {
|
|
9667
|
+
if (child.type === 'JSXText') {
|
|
9668
|
+
const text = this.normalizeJSXText(child.value);
|
|
9669
|
+
if (text) result.push(text);
|
|
9670
|
+
} else if (child.type === 'JSXExpressionContainer') {
|
|
9671
|
+
if (child.expression.type !== 'JSXEmptyExpression') {
|
|
9672
|
+
const value = await this.evaluateAsync(child.expression, env);
|
|
9673
|
+
if (Array.isArray(value)) {
|
|
9674
|
+
result.push(...value);
|
|
9675
|
+
} else if (value !== null && value !== undefined && value !== false) {
|
|
9676
|
+
result.push(value);
|
|
9677
|
+
}
|
|
9678
|
+
}
|
|
9679
|
+
} else if (child.type === 'JSXElement') {
|
|
9680
|
+
result.push(await this.evaluateJSXElementAsync(child, env));
|
|
9681
|
+
} else if (child.type === 'JSXFragment') {
|
|
9682
|
+
result.push(await this.evaluateJSXFragmentAsync(child, env));
|
|
9683
|
+
}
|
|
9684
|
+
}
|
|
9685
|
+
return result;
|
|
9686
|
+
}
|
|
8609
9687
|
}
|
|
8610
9688
|
|
|
8611
9689
|
// Built-in global objects and functions
|
|
@@ -8724,6 +9802,20 @@ function createGlobalEnvironment(env) {
|
|
|
8724
9802
|
env.define('SyntaxError', SyntaxError);
|
|
8725
9803
|
env.define('RangeError', RangeError);
|
|
8726
9804
|
|
|
9805
|
+
// JSX Runtime Support
|
|
9806
|
+
env.define('createElement', (type, props, ...children) => ({
|
|
9807
|
+
$$typeof: Symbol.for('react.element'),
|
|
9808
|
+
type,
|
|
9809
|
+
props: {
|
|
9810
|
+
...props,
|
|
9811
|
+
children: children.length === 0 ? undefined : children.length === 1 ? children[0] : children
|
|
9812
|
+
},
|
|
9813
|
+
key: props?.key ?? null,
|
|
9814
|
+
ref: props?.ref ?? null
|
|
9815
|
+
}));
|
|
9816
|
+
|
|
9817
|
+
env.define('Fragment', Symbol.for('react.fragment'));
|
|
9818
|
+
|
|
8727
9819
|
// Global console functions (shortcuts for console.log/warn/error)
|
|
8728
9820
|
env.define('log', (...args) => {
|
|
8729
9821
|
console.log(...args);
|
|
@@ -9228,7 +10320,8 @@ class WangInterpreter {
|
|
|
9228
10320
|
|
|
9229
10321
|
// Prepare execution options
|
|
9230
10322
|
const options = {
|
|
9231
|
-
moduleResolver: this.moduleResolver
|
|
10323
|
+
moduleResolver: this.moduleResolver,
|
|
10324
|
+
executionController: userOptions.executionController
|
|
9232
10325
|
// sourceType will be auto-detected from code
|
|
9233
10326
|
};
|
|
9234
10327
|
|
|
@@ -9327,6 +10420,219 @@ class InMemoryModuleResolver {
|
|
|
9327
10420
|
}
|
|
9328
10421
|
}
|
|
9329
10422
|
|
|
10423
|
+
/**
|
|
10424
|
+
* ExecutionController - Controls and monitors interpreter execution
|
|
10425
|
+
*
|
|
10426
|
+
* Provides pause/resume/abort capabilities and execution status introspection.
|
|
10427
|
+
* Works with async evaluation only - sync evaluation can only abort.
|
|
10428
|
+
*/
|
|
10429
|
+
class ExecutionController {
|
|
10430
|
+
constructor() {
|
|
10431
|
+
this.state = 'idle'; // 'idle' | 'running' | 'paused' | 'aborted' | 'completed'
|
|
10432
|
+
this.pauseRequested = false;
|
|
10433
|
+
this.abortRequested = false;
|
|
10434
|
+
|
|
10435
|
+
// Debug info
|
|
10436
|
+
this.stepCount = 0;
|
|
10437
|
+
this.currentNode = null;
|
|
10438
|
+
this.callStack = [];
|
|
10439
|
+
this.currentEnv = null;
|
|
10440
|
+
|
|
10441
|
+
this._resolveResume = null;
|
|
10442
|
+
}
|
|
10443
|
+
|
|
10444
|
+
// --- Control methods (called by user) ---
|
|
10445
|
+
|
|
10446
|
+
/**
|
|
10447
|
+
* Request pause at next checkpoint. Only works during async evaluation.
|
|
10448
|
+
*/
|
|
10449
|
+
pause() {
|
|
10450
|
+
if (this.state === 'running') {
|
|
10451
|
+
this.pauseRequested = true;
|
|
10452
|
+
}
|
|
10453
|
+
}
|
|
10454
|
+
|
|
10455
|
+
/**
|
|
10456
|
+
* Resume execution after pause.
|
|
10457
|
+
*/
|
|
10458
|
+
resume() {
|
|
10459
|
+
if (this.state === 'paused' && this._resolveResume) {
|
|
10460
|
+
this.pauseRequested = false;
|
|
10461
|
+
this._resolveResume();
|
|
10462
|
+
this._resolveResume = null;
|
|
10463
|
+
}
|
|
10464
|
+
}
|
|
10465
|
+
|
|
10466
|
+
/**
|
|
10467
|
+
* Abort execution. Works for both sync and async evaluation.
|
|
10468
|
+
* If paused, will resume and then abort.
|
|
10469
|
+
*/
|
|
10470
|
+
abort() {
|
|
10471
|
+
this.abortRequested = true;
|
|
10472
|
+
// Also resume if paused to allow abort to take effect
|
|
10473
|
+
if (this._resolveResume) {
|
|
10474
|
+
this._resolveResume();
|
|
10475
|
+
this._resolveResume = null;
|
|
10476
|
+
}
|
|
10477
|
+
}
|
|
10478
|
+
|
|
10479
|
+
// --- Status (called by user) ---
|
|
10480
|
+
|
|
10481
|
+
/**
|
|
10482
|
+
* Get current execution status with full debug information.
|
|
10483
|
+
* @returns {Object} Status object with state, stepCount, currentNode, callStack, and variables
|
|
10484
|
+
*/
|
|
10485
|
+
getStatus() {
|
|
10486
|
+
return {
|
|
10487
|
+
state: this.state,
|
|
10488
|
+
stepCount: this.stepCount,
|
|
10489
|
+
currentNode: this.currentNode?.type || null,
|
|
10490
|
+
callStack: [...this.callStack],
|
|
10491
|
+
variables: this._getEnvironmentVariables()
|
|
10492
|
+
};
|
|
10493
|
+
}
|
|
10494
|
+
|
|
10495
|
+
/**
|
|
10496
|
+
* Check if abort has been requested.
|
|
10497
|
+
* Compatible with AbortSignal interface.
|
|
10498
|
+
*/
|
|
10499
|
+
get aborted() {
|
|
10500
|
+
return this.abortRequested;
|
|
10501
|
+
}
|
|
10502
|
+
|
|
10503
|
+
// --- Internal methods (called by interpreter) ---
|
|
10504
|
+
|
|
10505
|
+
/**
|
|
10506
|
+
* Mark execution as started. Called by execute().
|
|
10507
|
+
* @internal
|
|
10508
|
+
*/
|
|
10509
|
+
_start() {
|
|
10510
|
+
this.state = 'running';
|
|
10511
|
+
this.stepCount = 0;
|
|
10512
|
+
this.currentNode = null;
|
|
10513
|
+
this.callStack = [];
|
|
10514
|
+
this.pauseRequested = false;
|
|
10515
|
+
// Note: don't reset abortRequested - allow pre-abort
|
|
10516
|
+
}
|
|
10517
|
+
|
|
10518
|
+
/**
|
|
10519
|
+
* Mark execution as completed. Called by execute().
|
|
10520
|
+
* @internal
|
|
10521
|
+
*/
|
|
10522
|
+
_complete() {
|
|
10523
|
+
this.state = 'completed';
|
|
10524
|
+
}
|
|
10525
|
+
|
|
10526
|
+
/**
|
|
10527
|
+
* Push a function call onto the call stack.
|
|
10528
|
+
* @internal
|
|
10529
|
+
*/
|
|
10530
|
+
_pushCall(name) {
|
|
10531
|
+
this.callStack.push(name);
|
|
10532
|
+
}
|
|
10533
|
+
|
|
10534
|
+
/**
|
|
10535
|
+
* Pop a function call from the call stack.
|
|
10536
|
+
* @internal
|
|
10537
|
+
*/
|
|
10538
|
+
_popCall() {
|
|
10539
|
+
this.callStack.pop();
|
|
10540
|
+
}
|
|
10541
|
+
|
|
10542
|
+
/**
|
|
10543
|
+
* Set the current environment for variable introspection.
|
|
10544
|
+
* @internal
|
|
10545
|
+
*/
|
|
10546
|
+
_setEnv(env) {
|
|
10547
|
+
this.currentEnv = env;
|
|
10548
|
+
}
|
|
10549
|
+
|
|
10550
|
+
/**
|
|
10551
|
+
* Async checkpoint - yields if paused, throws if aborted.
|
|
10552
|
+
* Called at coarse granularity points (loops, function calls).
|
|
10553
|
+
* @internal
|
|
10554
|
+
*/
|
|
10555
|
+
async _checkpoint(node) {
|
|
10556
|
+
this.stepCount++;
|
|
10557
|
+
this.currentNode = node;
|
|
10558
|
+
|
|
10559
|
+
if (this.abortRequested) {
|
|
10560
|
+
this.state = 'aborted';
|
|
10561
|
+
const error = new Error('The operation was aborted');
|
|
10562
|
+
error.name = 'AbortError';
|
|
10563
|
+
throw error;
|
|
10564
|
+
}
|
|
10565
|
+
|
|
10566
|
+
if (this.pauseRequested && this.state === 'running') {
|
|
10567
|
+
this.state = 'paused';
|
|
10568
|
+
await new Promise(resolve => { this._resolveResume = resolve; });
|
|
10569
|
+
this.state = 'running';
|
|
10570
|
+
|
|
10571
|
+
// Check abort after resume (user may have called abort while paused)
|
|
10572
|
+
if (this.abortRequested) {
|
|
10573
|
+
this.state = 'aborted';
|
|
10574
|
+
const error = new Error('The operation was aborted');
|
|
10575
|
+
error.name = 'AbortError';
|
|
10576
|
+
throw error;
|
|
10577
|
+
}
|
|
10578
|
+
}
|
|
10579
|
+
}
|
|
10580
|
+
|
|
10581
|
+
/**
|
|
10582
|
+
* Sync abort check - only throws if aborted, cannot pause.
|
|
10583
|
+
* Used in sync evaluate() path.
|
|
10584
|
+
* @internal
|
|
10585
|
+
*/
|
|
10586
|
+
_checkAbortSync() {
|
|
10587
|
+
if (this.abortRequested) {
|
|
10588
|
+
this.state = 'aborted';
|
|
10589
|
+
const error = new Error('The operation was aborted');
|
|
10590
|
+
error.name = 'AbortError';
|
|
10591
|
+
throw error;
|
|
10592
|
+
}
|
|
10593
|
+
}
|
|
10594
|
+
|
|
10595
|
+
/**
|
|
10596
|
+
* Get all variables from current environment chain.
|
|
10597
|
+
* @internal
|
|
10598
|
+
*/
|
|
10599
|
+
_getEnvironmentVariables() {
|
|
10600
|
+
if (!this.currentEnv) return {};
|
|
10601
|
+
const vars = {};
|
|
10602
|
+
let env = this.currentEnv;
|
|
10603
|
+
while (env) {
|
|
10604
|
+
if (env.vars) {
|
|
10605
|
+
for (const [key, value] of env.vars) {
|
|
10606
|
+
if (!(key in vars)) {
|
|
10607
|
+
vars[key] = this._serializeValue(value);
|
|
10608
|
+
}
|
|
10609
|
+
}
|
|
10610
|
+
}
|
|
10611
|
+
env = env.parent;
|
|
10612
|
+
}
|
|
10613
|
+
return vars;
|
|
10614
|
+
}
|
|
10615
|
+
|
|
10616
|
+
/**
|
|
10617
|
+
* Serialize a value for status reporting.
|
|
10618
|
+
* @internal
|
|
10619
|
+
*/
|
|
10620
|
+
_serializeValue(value) {
|
|
10621
|
+
if (value === undefined) return { type: 'undefined' };
|
|
10622
|
+
if (value === null) return { type: 'null' };
|
|
10623
|
+
if (typeof value === 'function' || (value && value.__isFunction)) {
|
|
10624
|
+
return { type: 'function', name: value.name || 'anonymous' };
|
|
10625
|
+
}
|
|
10626
|
+
if (Array.isArray(value)) {
|
|
10627
|
+
return { type: 'array', length: value.length };
|
|
10628
|
+
}
|
|
10629
|
+
if (typeof value === 'object') {
|
|
10630
|
+
return { type: 'object', preview: Object.keys(value).slice(0, 5) };
|
|
10631
|
+
}
|
|
10632
|
+
return { type: typeof value, value };
|
|
10633
|
+
}
|
|
10634
|
+
}
|
|
10635
|
+
|
|
9330
10636
|
// Use bundled Acorn parser for zero runtime dependencies
|
|
9331
10637
|
|
|
9332
10638
|
// Helper to detect if code contains module syntax or top-level await
|
|
@@ -9359,7 +10665,7 @@ function parse(code, options = {}) {
|
|
|
9359
10665
|
|
|
9360
10666
|
// Parse with Acorn
|
|
9361
10667
|
try {
|
|
9362
|
-
return
|
|
10668
|
+
return jsxParse(code, {
|
|
9363
10669
|
ecmaVersion: 2022, // Support ES2022 features (including top-level await)
|
|
9364
10670
|
sourceType: sourceType,
|
|
9365
10671
|
locations: true, // Track source locations for better error messages
|
|
@@ -9423,34 +10729,59 @@ function containsTopLevelAwait(node) {
|
|
|
9423
10729
|
}
|
|
9424
10730
|
|
|
9425
10731
|
async function execute(code, env = null, options = {}) {
|
|
9426
|
-
//
|
|
9427
|
-
const
|
|
10732
|
+
// Get execution controller if provided
|
|
10733
|
+
const controller = options.executionController;
|
|
9428
10734
|
|
|
9429
|
-
//
|
|
9430
|
-
if (
|
|
9431
|
-
|
|
10735
|
+
// Mark execution as starting
|
|
10736
|
+
if (controller) {
|
|
10737
|
+
controller._start();
|
|
9432
10738
|
}
|
|
9433
10739
|
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
abortSignal: options.abortSignal
|
|
9438
|
-
});
|
|
10740
|
+
try {
|
|
10741
|
+
// Parse the code
|
|
10742
|
+
const ast = parse(code, options);
|
|
9439
10743
|
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
10744
|
+
// Create global environment if not provided
|
|
10745
|
+
if (!env) {
|
|
10746
|
+
env = createGlobalEnvironment(new Environment());
|
|
10747
|
+
}
|
|
10748
|
+
|
|
10749
|
+
// Create interpreter with module resolver, abort signal, and execution controller
|
|
10750
|
+
const interpreter = new Interpreter(env, {
|
|
10751
|
+
moduleResolver: options.moduleResolver,
|
|
10752
|
+
abortSignal: options.abortSignal,
|
|
10753
|
+
executionController: controller
|
|
10754
|
+
});
|
|
10755
|
+
|
|
10756
|
+
// Use async evaluation if:
|
|
10757
|
+
// 1. Explicitly requested module mode
|
|
10758
|
+
// 2. AST contains import/export declarations
|
|
10759
|
+
// 3. Code contains top-level await
|
|
10760
|
+
// 4. Execution controller provided (needs async for pause/resume)
|
|
10761
|
+
const needsAsync = options.sourceType === 'module' ||
|
|
10762
|
+
containsModuleDeclarations(ast) ||
|
|
10763
|
+
containsTopLevelAwait(ast) ||
|
|
10764
|
+
controller != null;
|
|
10765
|
+
|
|
10766
|
+
if (needsAsync) {
|
|
10767
|
+
const result = await interpreter.evaluateAsync(ast, env);
|
|
10768
|
+
if (controller) {
|
|
10769
|
+
controller._complete();
|
|
10770
|
+
}
|
|
10771
|
+
return result instanceof ReturnValue ? result.value : result;
|
|
10772
|
+
} else {
|
|
10773
|
+
const result = interpreter.evaluate(ast, env);
|
|
10774
|
+
if (controller) {
|
|
10775
|
+
controller._complete();
|
|
10776
|
+
}
|
|
10777
|
+
return result instanceof ReturnValue ? result.value : result;
|
|
10778
|
+
}
|
|
10779
|
+
} catch (e) {
|
|
10780
|
+
// Mark as aborted if that's the error type
|
|
10781
|
+
if (controller && e.name === 'AbortError') {
|
|
10782
|
+
controller.state = 'aborted';
|
|
10783
|
+
}
|
|
10784
|
+
throw e;
|
|
9454
10785
|
}
|
|
9455
10786
|
}
|
|
9456
10787
|
|
|
@@ -9503,4 +10834,4 @@ class ModuleResolver {
|
|
|
9503
10834
|
* @property {any} [metadata] - Optional metadata about the module
|
|
9504
10835
|
*/
|
|
9505
10836
|
|
|
9506
|
-
export { Environment, InMemoryModuleResolver, Interpreter, ModuleResolver, WangInterpreter, createEnvironment, execute, isTopLevelAwait, parse };
|
|
10837
|
+
export { Environment, ExecutionController, InMemoryModuleResolver, Interpreter, ModuleResolver, WangInterpreter, createEnvironment, execute, isTopLevelAwait, parse };
|