jslike 1.3.1 → 1.4.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.
@@ -1,2155 +0,0 @@
1
- import { Environment, ReturnValue, BreakSignal, ContinueSignal, ThrowSignal } from '../runtime/environment.js';
2
- import { parse as acornParse } from '../parser.js';
3
- import { createMethodNotFoundError } from '../errors/enhanced-error.js';
4
-
5
- export class Interpreter {
6
- constructor(globalEnv, options = {}) {
7
- this.globalEnv = globalEnv;
8
- this.moduleResolver = options.moduleResolver;
9
- this.moduleCache = new Map(); // Cache loaded modules
10
- this.moduleExports = {}; // Track exports in current module
11
- this.abortSignal = options.abortSignal;
12
- }
13
-
14
- // Check if execution should be aborted
15
- checkAbortSignal() {
16
- if (this.abortSignal && this.abortSignal.aborted) {
17
- const error = new Error('The operation was aborted');
18
- error.name = 'AbortError';
19
- throw error;
20
- }
21
- }
22
-
23
- // Async evaluation for async functions - handles await expressions
24
- async evaluateAsync(node, env) {
25
- if (!node) return undefined;
26
-
27
- // Check for abort signal before evaluating
28
- this.checkAbortSignal();
29
-
30
- // Handle await expressions by actually awaiting the promise
31
- if (node.type === 'AwaitExpression') {
32
- const promise = await this.evaluateAsync(node.argument, env);
33
- return await promise;
34
- }
35
-
36
- // For block statements, evaluate each statement async
37
- if (node.type === 'BlockStatement') {
38
- const blockEnv = new Environment(env);
39
- let result = undefined;
40
- for (const statement of node.body) {
41
- result = await this.evaluateAsync(statement, blockEnv);
42
- if (result instanceof ReturnValue || result instanceof ThrowSignal ||
43
- result instanceof BreakSignal || result instanceof ContinueSignal) {
44
- return result;
45
- }
46
- }
47
- return result;
48
- }
49
-
50
- // For expression statements, evaluate the expression async
51
- if (node.type === 'ExpressionStatement') {
52
- return await this.evaluateAsync(node.expression, env);
53
- }
54
-
55
- // For variable declarations with await in init
56
- if (node.type === 'VariableDeclaration') {
57
- for (const declarator of node.declarations) {
58
- const value = declarator.init
59
- ? await this.evaluateAsync(declarator.init, env)
60
- : undefined;
61
-
62
- const isConst = node.kind === 'const';
63
- if (declarator.id.type === 'Identifier') {
64
- env.define(declarator.id.name, value, isConst);
65
- } else if (declarator.id.type === 'ObjectPattern') {
66
- this.bindObjectPattern(declarator.id, value, env, isConst);
67
- } else if (declarator.id.type === 'ArrayPattern') {
68
- this.bindArrayPattern(declarator.id, value, env, isConst);
69
- }
70
- }
71
- return undefined;
72
- }
73
-
74
- // For Program nodes (evaluate all statements async)
75
- if (node.type === 'Program') {
76
- let result = undefined;
77
- for (const statement of node.body) {
78
- result = await this.evaluateAsync(statement, env);
79
- // Handle top-level return and throw
80
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
81
- return result;
82
- }
83
- }
84
- return result;
85
- }
86
-
87
- // For import declarations (always async)
88
- if (node.type === 'ImportDeclaration') {
89
- return await this.evaluateImportDeclaration(node, env);
90
- }
91
-
92
- // For export declarations
93
- if (node.type === 'ExportNamedDeclaration') {
94
- return this.evaluateExportNamedDeclaration(node, env);
95
- }
96
-
97
- if (node.type === 'ExportDefaultDeclaration') {
98
- return this.evaluateExportDefaultDeclaration(node, env);
99
- }
100
-
101
- // For return statements with await
102
- if (node.type === 'ReturnStatement') {
103
- const value = node.argument ? await this.evaluateAsync(node.argument, env) : undefined;
104
- return new ReturnValue(value);
105
- }
106
-
107
- // For binary/unary expressions that might contain awaits
108
- if (node.type === 'BinaryExpression') {
109
- const left = await this.evaluateAsync(node.left, env);
110
- const right = await this.evaluateAsync(node.right, env);
111
- return this.evaluateBinaryExpressionValues(node.operator, left, right);
112
- }
113
-
114
- // For call expressions (might be calling async functions)
115
- if (node.type === 'CallExpression') {
116
- let thisContext = undefined;
117
- let callee;
118
- let objectName = null;
119
- let methodName = null;
120
-
121
- if (node.callee.type === 'MemberExpression') {
122
- thisContext = await this.evaluateAsync(node.callee.object, env);
123
- const prop = node.callee.computed
124
- ? await this.evaluateAsync(node.callee.property, env)
125
- : node.callee.property.name;
126
- callee = thisContext[prop];
127
-
128
- // Capture names for enhanced error messages
129
- methodName = prop;
130
- objectName = this.getExpressionName(node.callee.object);
131
- } else {
132
- callee = await this.evaluateAsync(node.callee, env);
133
- }
134
-
135
- // Handle optional call - if optional and callee is null/undefined, return undefined
136
- if (node.optional && (callee === null || callee === undefined)) {
137
- return undefined;
138
- }
139
-
140
- const args = [];
141
- for (const arg of node.arguments) {
142
- args.push(await this.evaluateAsync(arg, env));
143
- }
144
-
145
- if (typeof callee === 'function') {
146
- if (thisContext !== undefined) {
147
- return await callee.call(thisContext, ...args);
148
- }
149
- return await callee(...args);
150
- } else if (callee && callee.__isFunction) {
151
- return await this.callUserFunction(callee, args, env, thisContext);
152
- }
153
-
154
- // Throw enhanced error for member expression calls
155
- if (objectName && methodName) {
156
- throw createMethodNotFoundError(objectName, methodName, thisContext);
157
- }
158
-
159
- throw new TypeError(`${node.callee.name || 'Expression'} is not a function`);
160
- }
161
-
162
- // For chain expressions (optional chaining)
163
- if (node.type === 'ChainExpression') {
164
- return await this.evaluateAsync(node.expression, env);
165
- }
166
-
167
- // For member expressions in async context
168
- if (node.type === 'MemberExpression') {
169
- const obj = await this.evaluateAsync(node.object, env);
170
-
171
- // Handle optional chaining
172
- if (node.optional && (obj === null || obj === undefined)) {
173
- return undefined;
174
- }
175
-
176
- if (obj === null || obj === undefined) {
177
- throw new TypeError(`Cannot read property of ${obj}`);
178
- }
179
-
180
- const prop = node.computed
181
- ? await this.evaluateAsync(node.property, env)
182
- : node.property.name;
183
-
184
- return obj[prop];
185
- }
186
-
187
- // For template literals with await expressions
188
- if (node.type === 'TemplateLiteral') {
189
- let result = '';
190
- for (let i = 0; i < node.quasis.length; i++) {
191
- result += node.quasis[i].value.cooked || node.quasis[i].value.raw;
192
- if (i < node.expressions.length) {
193
- const exprValue = await this.evaluateAsync(node.expressions[i], env);
194
- result += String(exprValue);
195
- }
196
- }
197
- return result;
198
- }
199
-
200
- // For logical expressions with async operands (await support)
201
- if (node.type === 'LogicalExpression') {
202
- const left = await this.evaluateAsync(node.left, env);
203
-
204
- if (node.operator === '&&') {
205
- return left ? await this.evaluateAsync(node.right, env) : left;
206
- } else if (node.operator === '||') {
207
- return left ? left : await this.evaluateAsync(node.right, env);
208
- } else if (node.operator === '??') {
209
- return left !== null && left !== undefined ? left : await this.evaluateAsync(node.right, env);
210
- }
211
-
212
- throw new Error(`Unknown logical operator: ${node.operator}`);
213
- }
214
-
215
- // For try-catch-finally with async operations
216
- if (node.type === 'TryStatement') {
217
- let result;
218
-
219
- try {
220
- result = await this.evaluateAsync(node.block, env);
221
-
222
- if (result instanceof ThrowSignal) {
223
- throw result.value;
224
- }
225
- } catch (error) {
226
- if (node.handler) {
227
- const catchEnv = new Environment(env);
228
- if (node.handler.param) {
229
- catchEnv.define(node.handler.param.name, error);
230
- }
231
- result = await this.evaluateAsync(node.handler.body, catchEnv);
232
- } else {
233
- throw error;
234
- }
235
- } finally {
236
- if (node.finalizer) {
237
- const finalResult = await this.evaluateAsync(node.finalizer, env);
238
- // If finally block throws or returns, it overrides the try/catch result
239
- if (finalResult instanceof ThrowSignal || finalResult instanceof ReturnValue) {
240
- return finalResult;
241
- }
242
- }
243
- }
244
-
245
- return result;
246
- }
247
-
248
- // For new expressions (async constructors)
249
- if (node.type === 'NewExpression') {
250
- const result = this.evaluateNewExpression(node, env);
251
- // If it's a promise, await it
252
- if (result && typeof result.then === 'function') {
253
- return await result;
254
- }
255
- return result;
256
- }
257
-
258
- // For ForStatement with async body
259
- if (node.type === 'ForStatement') {
260
- const forEnv = new Environment(env);
261
- if (node.init) {
262
- await this.evaluateAsync(node.init, forEnv);
263
- }
264
- while (!node.test || await this.evaluateAsync(node.test, forEnv)) {
265
- const result = await this.evaluateAsync(node.body, forEnv);
266
- if (result instanceof BreakSignal) {
267
- break;
268
- }
269
- if (result instanceof ContinueSignal) {
270
- if (node.update) {
271
- await this.evaluateAsync(node.update, forEnv);
272
- }
273
- continue;
274
- }
275
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
276
- return result;
277
- }
278
- if (node.update) {
279
- await this.evaluateAsync(node.update, forEnv);
280
- }
281
- }
282
- return undefined;
283
- }
284
-
285
- // For ForOfStatement with async body
286
- if (node.type === 'ForOfStatement') {
287
- const forEnv = new Environment(env);
288
- const iterable = await this.evaluateAsync(node.right, forEnv);
289
- const declarator = node.left.declarations[0];
290
- const isConst = node.left.kind === 'const';
291
-
292
- for (const value of iterable) {
293
- const iterEnv = forEnv.extend();
294
- if (declarator.id.type === 'Identifier') {
295
- iterEnv.define(declarator.id.name, value, isConst);
296
- } else if (declarator.id.type === 'ArrayPattern') {
297
- this.bindArrayPattern(declarator.id, value, iterEnv, isConst);
298
- } else if (declarator.id.type === 'ObjectPattern') {
299
- this.bindObjectPattern(declarator.id, value, iterEnv, isConst);
300
- }
301
- const result = await this.evaluateAsync(node.body, iterEnv);
302
- if (result instanceof BreakSignal) {
303
- break;
304
- }
305
- if (result instanceof ContinueSignal) {
306
- continue;
307
- }
308
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
309
- return result;
310
- }
311
- }
312
- return undefined;
313
- }
314
-
315
- // For ForInStatement with async body
316
- if (node.type === 'ForInStatement') {
317
- const forEnv = new Environment(env);
318
- const obj = await this.evaluateAsync(node.right, forEnv);
319
- if (obj === null || obj === undefined) {
320
- throw new TypeError(`Cannot use 'in' operator to iterate over ${obj}`);
321
- }
322
- const varName = node.left.declarations[0].id.name;
323
- forEnv.define(varName, undefined);
324
-
325
- for (const key in obj) {
326
- forEnv.set(varName, key);
327
- const result = await this.evaluateAsync(node.body, forEnv);
328
- if (result instanceof BreakSignal) {
329
- break;
330
- }
331
- if (result instanceof ContinueSignal) {
332
- continue;
333
- }
334
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
335
- return result;
336
- }
337
- }
338
- return undefined;
339
- }
340
-
341
- // For WhileStatement with async body
342
- if (node.type === 'WhileStatement') {
343
- while (await this.evaluateAsync(node.test, env)) {
344
- const result = await this.evaluateAsync(node.body, env);
345
- if (result instanceof BreakSignal) {
346
- break;
347
- }
348
- if (result instanceof ContinueSignal) {
349
- continue;
350
- }
351
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
352
- return result;
353
- }
354
- }
355
- return undefined;
356
- }
357
-
358
- // For DoWhileStatement with async body
359
- if (node.type === 'DoWhileStatement') {
360
- do {
361
- const result = await this.evaluateAsync(node.body, env);
362
- if (result instanceof BreakSignal) {
363
- break;
364
- }
365
- if (result instanceof ContinueSignal) {
366
- continue;
367
- }
368
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
369
- return result;
370
- }
371
- } while (await this.evaluateAsync(node.test, env));
372
- return undefined;
373
- }
374
-
375
- // For IfStatement with async branches
376
- if (node.type === 'IfStatement') {
377
- const test = await this.evaluateAsync(node.test, env);
378
- if (test) {
379
- return await this.evaluateAsync(node.consequent, env);
380
- } else if (node.alternate) {
381
- return await this.evaluateAsync(node.alternate, env);
382
- }
383
- return undefined;
384
- }
385
-
386
- // For SwitchStatement with async cases
387
- if (node.type === 'SwitchStatement') {
388
- const discriminant = await this.evaluateAsync(node.discriminant, env);
389
- let matched = false;
390
-
391
- for (const switchCase of node.cases) {
392
- if (!matched && switchCase.test) {
393
- const testValue = await this.evaluateAsync(switchCase.test, env);
394
- if (testValue === discriminant) {
395
- matched = true;
396
- }
397
- } else if (!switchCase.test) {
398
- matched = true;
399
- }
400
-
401
- if (matched) {
402
- for (const statement of switchCase.consequent) {
403
- const result = await this.evaluateAsync(statement, env);
404
- if (result instanceof BreakSignal) {
405
- return undefined;
406
- }
407
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
408
- return result;
409
- }
410
- }
411
- }
412
- }
413
- return undefined;
414
- }
415
-
416
- // For ConditionalExpression (ternary) with async operands
417
- if (node.type === 'ConditionalExpression') {
418
- const test = await this.evaluateAsync(node.test, env);
419
- return test
420
- ? await this.evaluateAsync(node.consequent, env)
421
- : await this.evaluateAsync(node.alternate, env);
422
- }
423
-
424
- // For AssignmentExpression with async value
425
- if (node.type === 'AssignmentExpression') {
426
- const value = await this.evaluateAsync(node.right, env);
427
-
428
- if (node.left.type === 'Identifier') {
429
- const name = node.left.name;
430
- if (node.operator === '=') {
431
- if (env.has(name)) {
432
- env.set(name, value);
433
- } else {
434
- env.define(name, value);
435
- }
436
- return value;
437
- } else {
438
- const current = env.get(name);
439
- const newValue = this.applyCompoundAssignment(node.operator, current, value);
440
- env.set(name, newValue);
441
- return newValue;
442
- }
443
- } else if (node.left.type === 'MemberExpression') {
444
- const obj = await this.evaluateAsync(node.left.object, env);
445
- const prop = node.left.computed
446
- ? await this.evaluateAsync(node.left.property, env)
447
- : node.left.property.name;
448
-
449
- if (node.operator === '=') {
450
- obj[prop] = value;
451
- return value;
452
- } else {
453
- const newValue = this.applyCompoundAssignment(node.operator, obj[prop], value);
454
- obj[prop] = newValue;
455
- return newValue;
456
- }
457
- }
458
- throw new Error('Invalid assignment target');
459
- }
460
-
461
- // For UnaryExpression with async argument
462
- if (node.type === 'UnaryExpression') {
463
- if (node.operator === 'delete' && node.argument.type === 'MemberExpression') {
464
- const obj = await this.evaluateAsync(node.argument.object, env);
465
- const prop = node.argument.computed
466
- ? await this.evaluateAsync(node.argument.property, env)
467
- : node.argument.property.name;
468
- return delete obj[prop];
469
- }
470
- const argument = await this.evaluateAsync(node.argument, env);
471
- switch (node.operator) {
472
- case '+': return +argument;
473
- case '-': return -argument;
474
- case '!': return !argument;
475
- case '~': return ~argument;
476
- case 'typeof':
477
- if (argument && argument.__isFunction) {
478
- return 'function';
479
- }
480
- return typeof argument;
481
- case 'void': return undefined;
482
- case 'delete': return true;
483
- default:
484
- throw new Error(`Unknown unary operator: ${node.operator}`);
485
- }
486
- }
487
-
488
- // For UpdateExpression with async member access
489
- if (node.type === 'UpdateExpression') {
490
- if (node.argument.type === 'Identifier') {
491
- const name = node.argument.name;
492
- const current = env.get(name);
493
- const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
494
- const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
495
- env.set(name, newValue);
496
- return node.prefix ? newValue : numericCurrent;
497
- } else if (node.argument.type === 'MemberExpression') {
498
- const obj = await this.evaluateAsync(node.argument.object, env);
499
- if (obj === null || obj === undefined) {
500
- throw new TypeError(
501
- `Cannot read properties of ${obj} (reading '${
502
- node.argument.computed
503
- ? await this.evaluateAsync(node.argument.property, env)
504
- : node.argument.property.name
505
- }')`
506
- );
507
- }
508
- const prop = node.argument.computed
509
- ? await this.evaluateAsync(node.argument.property, env)
510
- : node.argument.property.name;
511
- let current = obj[prop];
512
- const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
513
- const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
514
- obj[prop] = newValue;
515
- return node.prefix ? newValue : numericCurrent;
516
- }
517
- throw new Error('Invalid update expression target');
518
- }
519
-
520
- // For ArrayExpression with async elements
521
- if (node.type === 'ArrayExpression') {
522
- const result = [];
523
- for (const elem of node.elements) {
524
- if (!elem) {
525
- result.push(undefined);
526
- } else if (elem.type === 'SpreadElement') {
527
- const spreadValue = await this.evaluateAsync(elem.argument, env);
528
- if (Array.isArray(spreadValue)) {
529
- result.push(...spreadValue);
530
- } else if (typeof spreadValue[Symbol.iterator] === 'function') {
531
- result.push(...spreadValue);
532
- } else {
533
- throw new TypeError('Spread syntax requires an iterable');
534
- }
535
- } else {
536
- result.push(await this.evaluateAsync(elem, env));
537
- }
538
- }
539
- return result;
540
- }
541
-
542
- // For ObjectExpression with async values
543
- if (node.type === 'ObjectExpression') {
544
- const obj = {};
545
- for (const prop of node.properties) {
546
- if (prop.type === 'SpreadElement') {
547
- const spreadValue = await this.evaluateAsync(prop.argument, env);
548
- if (typeof spreadValue === 'object' && spreadValue !== null) {
549
- Object.assign(obj, spreadValue);
550
- }
551
- } else {
552
- const key = prop.key.type === 'Identifier' && !prop.computed
553
- ? prop.key.name
554
- : await this.evaluateAsync(prop.key, env);
555
- const value = prop.value ? await this.evaluateAsync(prop.value, env) : env.get(key);
556
- if (prop.method && prop.value.type === 'FunctionExpression') {
557
- obj[key] = (...args) => {
558
- const funcValue = this.evaluate(prop.value, env);
559
- return this.callUserFunction(funcValue, args, env);
560
- };
561
- } else {
562
- obj[key] = value;
563
- }
564
- }
565
- }
566
- return obj;
567
- }
568
-
569
- // For SequenceExpression with async expressions
570
- if (node.type === 'SequenceExpression') {
571
- let result;
572
- for (const expr of node.expressions) {
573
- result = await this.evaluateAsync(expr, env);
574
- }
575
- return result;
576
- }
577
-
578
- // For ThrowStatement with async argument
579
- if (node.type === 'ThrowStatement') {
580
- return new ThrowSignal(await this.evaluateAsync(node.argument, env));
581
- }
582
-
583
- // For FunctionDeclaration - define in environment
584
- if (node.type === 'FunctionDeclaration') {
585
- return this.evaluateFunctionDeclaration(node, env);
586
- }
587
-
588
- // For FunctionExpression/ArrowFunctionExpression - create function
589
- if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
590
- return this.evaluateFunctionExpression(node, env);
591
- }
592
-
593
- // For ClassDeclaration
594
- if (node.type === 'ClassDeclaration') {
595
- return this.evaluateClassDeclaration(node, env);
596
- }
597
-
598
- // For ClassExpression
599
- if (node.type === 'ClassExpression') {
600
- return this.evaluateClassExpression(node, env);
601
- }
602
-
603
- // Only leaf nodes should fall through to sync evaluate
604
- // These have no sub-expressions that could contain await
605
- if (['Literal', 'Identifier', 'BreakStatement', 'ContinueStatement',
606
- 'EmptyStatement', 'ThisExpression', 'Super'].includes(node.type)) {
607
- return this.evaluate(node, env);
608
- }
609
-
610
- // Safety check - if we get here, we missed a node type
611
- throw new Error(`Unhandled node type in evaluateAsync: ${node.type}`);
612
- }
613
-
614
- evaluate(node, env) {
615
- if (!node) return undefined;
616
-
617
- // Check for abort signal before evaluating
618
- this.checkAbortSignal();
619
-
620
- switch (node.type) {
621
- case 'Program':
622
- return this.evaluateProgram(node, env);
623
-
624
- case 'Literal':
625
- // Handle regex literals
626
- if (node.regex) {
627
- return new RegExp(node.regex.pattern, node.regex.flags);
628
- }
629
- return node.value;
630
-
631
- case 'Identifier':
632
- return env.get(node.name);
633
-
634
- case 'BinaryExpression':
635
- return this.evaluateBinaryExpression(node, env);
636
-
637
- case 'UnaryExpression':
638
- return this.evaluateUnaryExpression(node, env);
639
-
640
- case 'UpdateExpression':
641
- return this.evaluateUpdateExpression(node, env);
642
-
643
- case 'AwaitExpression':
644
- return this.evaluateAwaitExpression(node, env);
645
-
646
- case 'AssignmentExpression':
647
- return this.evaluateAssignmentExpression(node, env);
648
-
649
- case 'LogicalExpression':
650
- return this.evaluateLogicalExpression(node, env);
651
-
652
- case 'ConditionalExpression':
653
- return this.evaluateConditionalExpression(node, env);
654
-
655
- case 'CallExpression':
656
- return this.evaluateCallExpression(node, env);
657
-
658
- case 'MemberExpression':
659
- return this.evaluateMemberExpression(node, env);
660
-
661
- case 'ChainExpression':
662
- return this.evaluateChainExpression(node, env);
663
-
664
- case 'ArrayExpression':
665
- return this.evaluateArrayExpression(node, env);
666
-
667
- case 'ObjectExpression':
668
- return this.evaluateObjectExpression(node, env);
669
-
670
- case 'FunctionExpression':
671
- case 'ArrowFunctionExpression':
672
- return this.evaluateFunctionExpression(node, env);
673
-
674
- case 'NewExpression':
675
- return this.evaluateNewExpression(node, env);
676
-
677
- case 'ThisExpression':
678
- return this.evaluateThisExpression(node, env);
679
-
680
- case 'Super':
681
- return this.evaluateSuperExpression(node, env);
682
-
683
- case 'SequenceExpression':
684
- return this.evaluateSequenceExpression(node, env);
685
-
686
- case 'VariableDeclaration':
687
- return this.evaluateVariableDeclaration(node, env);
688
-
689
- case 'FunctionDeclaration':
690
- return this.evaluateFunctionDeclaration(node, env);
691
-
692
- case 'ImportDeclaration':
693
- return this.evaluateImportDeclaration(node, env);
694
-
695
- case 'ExportNamedDeclaration':
696
- return this.evaluateExportNamedDeclaration(node, env);
697
-
698
- case 'ExportDefaultDeclaration':
699
- return this.evaluateExportDefaultDeclaration(node, env);
700
-
701
- case 'BlockStatement':
702
- return this.evaluateBlockStatement(node, env);
703
-
704
- case 'ExpressionStatement':
705
- return this.evaluate(node.expression, env);
706
-
707
- case 'ReturnStatement':
708
- return new ReturnValue(node.argument ? this.evaluate(node.argument, env) : undefined);
709
-
710
- case 'IfStatement':
711
- return this.evaluateIfStatement(node, env);
712
-
713
- case 'WhileStatement':
714
- return this.evaluateWhileStatement(node, env);
715
-
716
- case 'DoWhileStatement':
717
- return this.evaluateDoWhileStatement(node, env);
718
-
719
- case 'ForStatement':
720
- return this.evaluateForStatement(node, env);
721
-
722
- case 'ForInStatement':
723
- return this.evaluateForInStatement(node, env);
724
-
725
- case 'ForOfStatement':
726
- return this.evaluateForOfStatement(node, env);
727
-
728
- case 'BreakStatement':
729
- return new BreakSignal();
730
-
731
- case 'ContinueStatement':
732
- return new ContinueSignal();
733
-
734
- case 'ThrowStatement':
735
- return new ThrowSignal(this.evaluate(node.argument, env));
736
-
737
- case 'TryStatement':
738
- return this.evaluateTryStatement(node, env);
739
-
740
- case 'SwitchStatement':
741
- return this.evaluateSwitchStatement(node, env);
742
-
743
- case 'EmptyStatement':
744
- return undefined;
745
-
746
- // ES6+ Features
747
- case 'TemplateLiteral':
748
- return this.evaluateTemplateLiteral(node, env);
749
-
750
- case 'ClassDeclaration':
751
- return this.evaluateClassDeclaration(node, env);
752
-
753
- case 'ClassExpression':
754
- return this.evaluateClassExpression(node, env);
755
-
756
- case 'MethodDefinition':
757
- return this.evaluateMethodDefinition(node, env);
758
-
759
- case 'SpreadElement':
760
- return this.evaluateSpreadElement(node, env);
761
-
762
- case 'RestElement':
763
- return this.evaluateRestElement(node, env);
764
-
765
- case 'ObjectPattern':
766
- return this.evaluateObjectPattern(node, env);
767
-
768
- case 'ArrayPattern':
769
- return this.evaluateArrayPattern(node, env);
770
-
771
- case 'AssignmentPattern':
772
- return this.evaluateAssignmentPattern(node, env);
773
-
774
- case 'Property':
775
- return this.evaluateProperty(node, env);
776
-
777
- default:
778
- throw new Error(`Unknown node type: ${node.type}`);
779
- }
780
- }
781
-
782
- evaluateProgram(node, env) {
783
- let result = undefined;
784
- for (let i = 0; i < node.body.length; i++) {
785
- const statement = node.body[i];
786
- const isLast = i === node.body.length - 1;
787
-
788
- // Special case: Last statement is a BlockStatement that looks like object literal
789
- // Handle both shorthand { x, y } and full syntax { key: value, key2: value2 }
790
- if (isLast && statement.type === 'BlockStatement') {
791
- const objLiteral = this.tryConvertBlockToObjectLiteral(statement, env);
792
- if (objLiteral !== null) {
793
- return objLiteral;
794
- }
795
- }
796
-
797
- const statementResult = this.evaluate(statement, env);
798
- if (statementResult instanceof ReturnValue || statementResult instanceof ThrowSignal) {
799
- return statementResult;
800
- }
801
- result = statementResult;
802
- }
803
- return result;
804
- }
805
-
806
- // Try to convert a BlockStatement to an object literal
807
- // Returns null if the block doesn't look like an object literal
808
- tryConvertBlockToObjectLiteral(block, env) {
809
- if (block.body.length === 0) return null;
810
-
811
- // Check if it's shorthand syntax: { x, y }
812
- if (block.body.length === 1 && block.body[0].type === 'ExpressionStatement') {
813
- const expr = block.body[0].expression;
814
-
815
- // SequenceExpression of Identifiers: { x, y, z }
816
- if (expr.type === 'SequenceExpression' &&
817
- expr.expressions.every(e => e.type === 'Identifier')) {
818
- const obj = {};
819
- for (const identifier of expr.expressions) {
820
- obj[identifier.name] = env.get(identifier.name);
821
- }
822
- return obj;
823
- }
824
-
825
- // Single Identifier: { x }
826
- if (expr.type === 'Identifier') {
827
- const obj = {};
828
- obj[expr.name] = env.get(expr.name);
829
- return obj;
830
- }
831
- }
832
-
833
- // Check if it's labeled statements that look like object properties
834
- // Example: { first: first(arr), last: last(arr) }
835
- // This gets parsed as LabeledStatements in script mode
836
- const allLabeled = block.body.every(stmt => stmt.type === 'LabeledStatement');
837
- if (!allLabeled) return null;
838
-
839
- // Convert labeled statements to object properties
840
- const obj = {};
841
- for (const stmt of block.body) {
842
- const label = stmt.label.name;
843
-
844
- // The body of LabeledStatement should be ExpressionStatement
845
- if (stmt.body.type !== 'ExpressionStatement') {
846
- return null; // Not an object literal pattern
847
- }
848
-
849
- const value = this.evaluate(stmt.body.expression, env);
850
- obj[label] = value;
851
- }
852
-
853
- return obj;
854
- }
855
-
856
- evaluateBinaryExpression(node, env) {
857
- const left = this.evaluate(node.left, env);
858
- const right = this.evaluate(node.right, env);
859
- return this.evaluateBinaryExpressionValues(node.operator, left, right);
860
- }
861
-
862
- evaluateBinaryExpressionValues(operator, left, right) {
863
- switch (operator) {
864
- case '+': return left + right;
865
- case '-': return left - right;
866
- case '*': return left * right;
867
- case '/': return left / right;
868
- case '%': return left % right;
869
- case '**': return left ** right;
870
- case '<': return left < right;
871
- case '>': return left > right;
872
- case '<=': return left <= right;
873
- case '>=': return left >= right;
874
- case '==': return left == right;
875
- case '!=': return left != right;
876
- case '===': return left === right;
877
- case '!==': return left !== right;
878
- case '&': return left & right;
879
- case '|': return left | right;
880
- case '^': return left ^ right;
881
- case '<<': return left << right;
882
- case '>>': return left >> right;
883
- case '>>>': return left >>> right;
884
- case 'in': {
885
- // Check right operand is not null/undefined
886
- if (right === null || right === undefined) {
887
- throw new TypeError(
888
- 'Cannot use "in" operator to search for property in null or undefined'
889
- );
890
- }
891
- // Coerce left operand to string/symbol for property key
892
- const key = String(left);
893
- return key in Object(right);
894
- }
895
- case 'instanceof': {
896
- // Check right operand is a constructor function or JSLike function
897
- if (typeof right !== 'function' && !(right && right.__isFunction)) {
898
- throw new TypeError(
899
- 'Right-hand side of instanceof is not a constructor'
900
- );
901
- }
902
- // Primitives (null/undefined) always return false
903
- if (left === null || left === undefined) {
904
- return false;
905
- }
906
- // Special case: check if left is a JSLike function and right is Function constructor
907
- if (right === Function && left && left.__isFunction) {
908
- return true;
909
- }
910
- // Use JavaScript's instanceof for native objects
911
- if (typeof right === 'function') {
912
- return left instanceof right;
913
- }
914
- // For JSLike functions, check prototype chain
915
- return false;
916
- }
917
- default:
918
- throw new Error(`Unknown binary operator: ${operator}`);
919
- }
920
- }
921
-
922
- evaluateUnaryExpression(node, env) {
923
- const argument = this.evaluate(node.argument, env);
924
-
925
- switch (node.operator) {
926
- case '+': return +argument;
927
- case '-': return -argument;
928
- case '!': return !argument;
929
- case '~': return ~argument;
930
- case 'typeof':
931
- // JSLike functions should report as 'function'
932
- if (argument && argument.__isFunction) {
933
- return 'function';
934
- }
935
- return typeof argument;
936
- case 'void': return undefined;
937
- case 'delete':
938
- if (node.argument.type === 'MemberExpression') {
939
- const obj = this.evaluate(node.argument.object, env);
940
- const prop = node.argument.computed
941
- ? this.evaluate(node.argument.property, env)
942
- : node.argument.property.name;
943
- return delete obj[prop];
944
- }
945
- return true;
946
- default:
947
- throw new Error(`Unknown unary operator: ${node.operator}`);
948
- }
949
- }
950
-
951
- evaluateUpdateExpression(node, env) {
952
- if (node.argument.type === 'Identifier') {
953
- const name = node.argument.name;
954
- const current = env.get(name);
955
- // Wang feature: treat null/undefined as 0 for increment/decrement
956
- const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
957
- const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
958
- env.set(name, newValue);
959
- return node.prefix ? newValue : numericCurrent;
960
- } else if (node.argument.type === 'MemberExpression') {
961
- const obj = this.evaluate(node.argument.object, env);
962
-
963
- // Check for null/undefined object
964
- if (obj === null || obj === undefined) {
965
- throw new TypeError(
966
- `Cannot read properties of ${obj} (reading '${
967
- node.argument.computed
968
- ? this.evaluate(node.argument.property, env)
969
- : node.argument.property.name
970
- }')`
971
- );
972
- }
973
-
974
- const prop = node.argument.computed
975
- ? this.evaluate(node.argument.property, env)
976
- : node.argument.property.name;
977
-
978
- // Get current value and convert to number
979
- let current = obj[prop];
980
- // Wang feature: treat null/undefined as 0 for increment/decrement
981
- const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
982
- const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
983
- obj[prop] = newValue;
984
-
985
- return node.prefix ? newValue : numericCurrent;
986
- }
987
- throw new Error('Invalid update expression target');
988
- }
989
-
990
- evaluateAwaitExpression(node, env) {
991
- // Evaluate the argument (should be a Promise)
992
- const promise = this.evaluate(node.argument, env);
993
-
994
- // Return the promise - the caller must handle it
995
- // This is a simplified implementation that relies on the runtime being async
996
- return promise;
997
- }
998
-
999
- evaluateAssignmentExpression(node, env) {
1000
- const value = this.evaluate(node.right, env);
1001
-
1002
- if (node.left.type === 'Identifier') {
1003
- const name = node.left.name;
1004
-
1005
- if (node.operator === '=') {
1006
- if (env.has(name)) {
1007
- env.set(name, value);
1008
- } else {
1009
- env.define(name, value);
1010
- }
1011
- return value;
1012
- } else {
1013
- const current = env.get(name);
1014
- const newValue = this.applyCompoundAssignment(node.operator, current, value);
1015
- env.set(name, newValue);
1016
- return newValue;
1017
- }
1018
- } else if (node.left.type === 'MemberExpression') {
1019
- const obj = this.evaluate(node.left.object, env);
1020
- const prop = node.left.computed
1021
- ? this.evaluate(node.left.property, env)
1022
- : node.left.property.name;
1023
-
1024
- if (node.operator === '=') {
1025
- obj[prop] = value;
1026
- return value;
1027
- } else {
1028
- const newValue = this.applyCompoundAssignment(node.operator, obj[prop], value);
1029
- obj[prop] = newValue;
1030
- return newValue;
1031
- }
1032
- }
1033
-
1034
- throw new Error('Invalid assignment target');
1035
- }
1036
-
1037
- applyCompoundAssignment(operator, left, right) {
1038
- // For numeric operators, coerce undefined to 0 (like JavaScript does for += with numbers)
1039
- // But keep undefined for string concatenation
1040
- const isNumericOp = operator !== '+=';
1041
- const leftVal = (isNumericOp && left === undefined) ? 0 : left;
1042
-
1043
- switch (operator) {
1044
- case '+=':
1045
- // Special case: undefined + number should coerce undefined to 0
1046
- if (left === undefined && typeof right === 'number') {
1047
- return 0 + right;
1048
- }
1049
- return left + right;
1050
- case '-=': return leftVal - right;
1051
- case '*=': return leftVal * right;
1052
- case '/=': return leftVal / right;
1053
- case '%=': return leftVal % right;
1054
- default: throw new Error(`Unknown assignment operator: ${operator}`);
1055
- }
1056
- }
1057
-
1058
- evaluateLogicalExpression(node, env) {
1059
- const left = this.evaluate(node.left, env);
1060
-
1061
- if (node.operator === '&&') {
1062
- return left ? this.evaluate(node.right, env) : left;
1063
- } else if (node.operator === '||') {
1064
- return left ? left : this.evaluate(node.right, env);
1065
- } else if (node.operator === '??') {
1066
- return left !== null && left !== undefined ? left : this.evaluate(node.right, env);
1067
- }
1068
-
1069
- throw new Error(`Unknown logical operator: ${node.operator}`);
1070
- }
1071
-
1072
- evaluateConditionalExpression(node, env) {
1073
- const test = this.evaluate(node.test, env);
1074
- return test
1075
- ? this.evaluate(node.consequent, env)
1076
- : this.evaluate(node.alternate, env);
1077
- }
1078
-
1079
- evaluateCallExpression(node, env) {
1080
- // Determine thisContext for method calls
1081
- let thisContext = undefined;
1082
- let callee;
1083
- let objectName = null;
1084
- let methodName = null;
1085
-
1086
- if (node.callee.type === 'MemberExpression') {
1087
- // For method calls like obj.method(), set this to obj
1088
- thisContext = this.evaluate(node.callee.object, env);
1089
- const prop = node.callee.computed
1090
- ? this.evaluate(node.callee.property, env)
1091
- : node.callee.property.name;
1092
- callee = thisContext[prop];
1093
-
1094
- // Capture names for enhanced error messages
1095
- methodName = prop;
1096
- objectName = this.getExpressionName(node.callee.object);
1097
- } else {
1098
- callee = this.evaluate(node.callee, env);
1099
- }
1100
-
1101
- // Handle optional call - if optional and callee is null/undefined, return undefined
1102
- if (node.optional && (callee === null || callee === undefined)) {
1103
- return undefined;
1104
- }
1105
-
1106
- const args = node.arguments.map(arg => this.evaluate(arg, env));
1107
-
1108
- if (typeof callee === 'function') {
1109
- // Native JavaScript function or class method
1110
- if (thisContext !== undefined) {
1111
- return callee.call(thisContext, ...args);
1112
- }
1113
- return callee(...args);
1114
- } else if (callee && callee.__isFunction) {
1115
- // User-defined function - pass thisContext
1116
- return this.callUserFunction(callee, args, env, thisContext);
1117
- }
1118
-
1119
- // Throw enhanced error for member expression calls
1120
- if (objectName && methodName) {
1121
- throw createMethodNotFoundError(objectName, methodName, thisContext);
1122
- }
1123
-
1124
- throw new TypeError(`${node.callee.name || 'Expression'} is not a function`);
1125
- }
1126
-
1127
- // Helper to get a readable name for an expression (for error messages)
1128
- getExpressionName(node) {
1129
- if (!node) return 'object';
1130
-
1131
- switch (node.type) {
1132
- case 'Identifier':
1133
- return node.name;
1134
- case 'ThisExpression':
1135
- return 'this';
1136
- case 'MemberExpression':
1137
- const objName = this.getExpressionName(node.object);
1138
- const propName = node.computed ? '[...]' : node.property.name;
1139
- return `${objName}.${propName}`;
1140
- default:
1141
- return 'object';
1142
- }
1143
- }
1144
-
1145
- callUserFunction(func, args, callingEnv, thisContext = undefined) {
1146
- // Extract metadata if function is wrapped
1147
- const metadata = func.__metadata || func;
1148
- const funcEnv = new Environment(metadata.closure);
1149
-
1150
- // Bind 'this' if provided (for method calls)
1151
- if (thisContext !== undefined) {
1152
- funcEnv.define('this', thisContext);
1153
- }
1154
-
1155
- // Bind parameters
1156
- for (let i = 0; i < metadata.params.length; i++) {
1157
- const param = metadata.params[i];
1158
-
1159
- if (param.type === 'Identifier') {
1160
- // Simple parameter: function(x)
1161
- funcEnv.define(param.name, args[i]);
1162
- } else if (param.type === 'AssignmentPattern') {
1163
- // Default parameter: function(x = defaultValue)
1164
- const value = args[i] !== undefined ? args[i] : this.evaluate(param.right, funcEnv);
1165
- funcEnv.define(param.left.name, value);
1166
- } else if (param.type === 'RestElement') {
1167
- // Rest parameter: function(...rest)
1168
- funcEnv.define(param.argument.name, args.slice(i));
1169
- break; // Rest element must be last
1170
- } else if (param.type === 'ObjectPattern') {
1171
- // Destructuring parameter: function({a, b})
1172
- this.bindObjectPattern(param, args[i], funcEnv);
1173
- } else if (param.type === 'ArrayPattern') {
1174
- // Array destructuring parameter: function([a, b])
1175
- this.bindArrayPattern(param, args[i], funcEnv);
1176
- } else {
1177
- // Fallback for simple parameter names
1178
- funcEnv.define(param.name, args[i]);
1179
- }
1180
- }
1181
-
1182
- // Execute function body
1183
- // If async, use async evaluation and return a promise
1184
- if (metadata.async) {
1185
- return (async () => {
1186
- if (metadata.expression) {
1187
- // Arrow function with expression body
1188
- const result = await this.evaluateAsync(metadata.body, funcEnv);
1189
- // If the result is a ThrowSignal, throw the error
1190
- if (result instanceof ThrowSignal) {
1191
- throw result.value;
1192
- }
1193
- return result;
1194
- } else {
1195
- // Block statement body
1196
- const result = await this.evaluateAsync(metadata.body, funcEnv);
1197
- if (result instanceof ReturnValue) {
1198
- return result.value;
1199
- }
1200
- // If the result is a ThrowSignal, throw the error
1201
- if (result instanceof ThrowSignal) {
1202
- throw result.value;
1203
- }
1204
- return undefined;
1205
- }
1206
- })();
1207
- } else {
1208
- // Synchronous evaluation for non-async functions
1209
- if (metadata.expression) {
1210
- const result = this.evaluate(metadata.body, funcEnv);
1211
- // If the result is a ThrowSignal, throw the error
1212
- if (result instanceof ThrowSignal) {
1213
- throw result.value;
1214
- }
1215
- return result;
1216
- } else {
1217
- const result = this.evaluate(metadata.body, funcEnv);
1218
- if (result instanceof ReturnValue) {
1219
- return result.value;
1220
- }
1221
- // If the result is a ThrowSignal, throw the error
1222
- if (result instanceof ThrowSignal) {
1223
- throw result.value;
1224
- }
1225
- return undefined;
1226
- }
1227
- }
1228
- }
1229
-
1230
- evaluateMemberExpression(node, env) {
1231
- const obj = this.evaluate(node.object, env);
1232
-
1233
- // Handle optional chaining - if optional and obj is null/undefined, return undefined
1234
- if (node.optional && (obj === null || obj === undefined)) {
1235
- return undefined;
1236
- }
1237
-
1238
- if (obj === null || obj === undefined) {
1239
- throw new TypeError(`Cannot read property of ${obj}`);
1240
- }
1241
-
1242
- const prop = node.computed
1243
- ? this.evaluate(node.property, env)
1244
- : node.property.name;
1245
-
1246
- return obj[prop];
1247
- }
1248
-
1249
- evaluateChainExpression(node, env) {
1250
- // ChainExpression is a wrapper for optional chaining expressions
1251
- // It contains the actual expression (MemberExpression or CallExpression with optional: true)
1252
- // We just evaluate the inner expression, which will handle the optional logic
1253
- return this.evaluate(node.expression, env);
1254
- }
1255
-
1256
- evaluateArrayExpression(node, env) {
1257
- const result = [];
1258
- for (const elem of node.elements) {
1259
- if (!elem) {
1260
- // Hole in array [1, , 3]
1261
- result.push(undefined);
1262
- } else if (elem.type === 'SpreadElement') {
1263
- // Spread syntax [...arr]
1264
- const spreadValue = this.evaluate(elem.argument, env);
1265
- if (Array.isArray(spreadValue)) {
1266
- result.push(...spreadValue);
1267
- } else if (typeof spreadValue[Symbol.iterator] === 'function') {
1268
- result.push(...spreadValue);
1269
- } else {
1270
- throw new TypeError('Spread syntax requires an iterable');
1271
- }
1272
- } else {
1273
- result.push(this.evaluate(elem, env));
1274
- }
1275
- }
1276
- return result;
1277
- }
1278
-
1279
- evaluateObjectExpression(node, env) {
1280
- const obj = {};
1281
- for (const prop of node.properties) {
1282
- if (prop.type === 'SpreadElement') {
1283
- // Object spread {...other}
1284
- const spreadValue = this.evaluate(prop.argument, env);
1285
- if (typeof spreadValue === 'object' && spreadValue !== null) {
1286
- Object.assign(obj, spreadValue);
1287
- }
1288
- } else {
1289
- // Regular property or shorthand
1290
- const key = prop.key.type === 'Identifier' && !prop.computed
1291
- ? prop.key.name
1292
- : this.evaluate(prop.key, env);
1293
-
1294
- // Handle shorthand properties {x} => {x: x}
1295
- const value = prop.value ? this.evaluate(prop.value, env) : env.get(key);
1296
-
1297
- // Handle method shorthand: method() {}
1298
- if (prop.method && prop.value.type === 'FunctionExpression') {
1299
- obj[key] = (...args) => {
1300
- const funcValue = this.evaluate(prop.value, env);
1301
- return this.callUserFunction(funcValue, args, env);
1302
- };
1303
- } else {
1304
- obj[key] = value;
1305
- }
1306
- }
1307
- }
1308
- return obj;
1309
- }
1310
-
1311
- evaluateFunctionExpression(node, env) {
1312
- const funcMetadata = {
1313
- __isFunction: true,
1314
- params: node.params,
1315
- body: node.body,
1316
- closure: env,
1317
- expression: node.type === 'ArrowFunctionExpression' && node.expression,
1318
- async: node.async || false
1319
- };
1320
-
1321
- // Wrap in actual JavaScript function so it can be called by native code
1322
- const interpreter = this;
1323
-
1324
- // Create async or sync wrapper based on function type
1325
- // Note: using regular function (not arrow) to capture 'this' for method calls
1326
- const wrappedFunc = funcMetadata.async
1327
- ? async function(...args) {
1328
- return await interpreter.callUserFunction(funcMetadata, args, funcMetadata.closure, this);
1329
- }
1330
- : function(...args) {
1331
- return interpreter.callUserFunction(funcMetadata, args, funcMetadata.closure, this);
1332
- };
1333
-
1334
- // Preserve metadata for JSLike's internal use
1335
- wrappedFunc.__isFunction = true;
1336
- wrappedFunc.__metadata = funcMetadata;
1337
-
1338
- return wrappedFunc;
1339
- }
1340
-
1341
- evaluateNewExpression(node, env) {
1342
- const constructor = this.evaluate(node.callee, env);
1343
- const args = node.arguments.map(arg => this.evaluate(arg, env));
1344
-
1345
- // Handle user-defined functions (including async and arrow functions)
1346
- if (constructor && constructor.__isFunction) {
1347
- const result = this.callUserFunction(constructor, args, env);
1348
- // If result is a promise (async function), return it directly
1349
- // The async context will handle it
1350
- if (result && typeof result.then === 'function') {
1351
- return result.then(res => {
1352
- if (res && typeof res === 'object') {
1353
- return res;
1354
- }
1355
- return {};
1356
- });
1357
- }
1358
- // If the function returns an object, use it; otherwise create a new object
1359
- if (result && typeof result === 'object') {
1360
- return result;
1361
- }
1362
- // For arrow/async functions that don't return an object, create one
1363
- return {};
1364
- }
1365
-
1366
- if (typeof constructor === 'function') {
1367
- // For native functions and classes, try to construct
1368
- // Arrow functions and async functions can't be constructed with 'new' in JavaScript,
1369
- // but if they return an object, we can use that
1370
- try {
1371
- return new constructor(...args);
1372
- } catch (err) {
1373
- // If construction fails (e.g., arrow function, async function),
1374
- // try calling it normally and see if it returns an object
1375
- if (err.message && err.message.includes('not a constructor')) {
1376
- const result = constructor(...args);
1377
- // If result is a promise, handle it
1378
- if (result && typeof result.then === 'function') {
1379
- return result.then(res => {
1380
- if (res && typeof res === 'object') {
1381
- return res;
1382
- }
1383
- throw new TypeError(`Type mismatch in new expression: ${node.callee.name || 'Expression'} is not a constructor`);
1384
- });
1385
- }
1386
- if (result && typeof result === 'object') {
1387
- return result;
1388
- }
1389
- throw new TypeError(`Type mismatch in new expression: ${node.callee.name || 'Expression'} is not a constructor`);
1390
- }
1391
- throw err;
1392
- }
1393
- }
1394
-
1395
- throw new TypeError(`Type mismatch in new expression: ${node.callee.name || 'Expression'} is not a constructor`);
1396
- }
1397
-
1398
- evaluateThisExpression(node, env) {
1399
- try {
1400
- return env.get('this');
1401
- } catch (e) {
1402
- // 'this' not defined in current scope
1403
- return undefined;
1404
- }
1405
- }
1406
-
1407
- evaluateSuperExpression(node, env) {
1408
- // Super is used in class methods to access parent class
1409
- try {
1410
- return env.get('super');
1411
- } catch (e) {
1412
- throw new ReferenceError("'super' keyword is unexpected here");
1413
- }
1414
- }
1415
-
1416
- evaluateSequenceExpression(node, env) {
1417
- let result;
1418
- for (const expr of node.expressions) {
1419
- result = this.evaluate(expr, env);
1420
- }
1421
- return result;
1422
- }
1423
-
1424
- evaluateVariableDeclaration(node, env) {
1425
- const isConst = node.kind === 'const';
1426
-
1427
- for (const declarator of node.declarations) {
1428
- const value = declarator.init
1429
- ? this.evaluate(declarator.init, env)
1430
- : undefined;
1431
-
1432
- // Handle destructuring patterns
1433
- if (declarator.id.type === 'ObjectPattern') {
1434
- this.bindObjectPattern(declarator.id, value, env, isConst);
1435
- } else if (declarator.id.type === 'ArrayPattern') {
1436
- this.bindArrayPattern(declarator.id, value, env, isConst);
1437
- } else {
1438
- env.define(declarator.id.name, value, isConst);
1439
- }
1440
- }
1441
- return undefined;
1442
- }
1443
-
1444
- bindObjectPattern(pattern, value, env, isConst = false) {
1445
- if (value === null || value === undefined) {
1446
- throw new TypeError('Cannot destructure undefined or null');
1447
- }
1448
-
1449
- for (const prop of pattern.properties) {
1450
- if (prop.type === 'RestElement') {
1451
- // Handle rest properties {...rest}
1452
- const assignedKeys = pattern.properties
1453
- .filter(p => p.type !== 'RestElement')
1454
- .map(p => p.key.name || p.key.value);
1455
- const restObj = {};
1456
- for (const key in value) {
1457
- if (!assignedKeys.includes(key)) {
1458
- restObj[key] = value[key];
1459
- }
1460
- }
1461
- env.define(prop.argument.name, restObj, isConst);
1462
- } else {
1463
- const key = prop.key.name || prop.key.value;
1464
- const propValue = value[key];
1465
-
1466
- if (prop.value.type === 'Identifier') {
1467
- env.define(prop.value.name, propValue, isConst);
1468
- } else if (prop.value.type === 'AssignmentPattern') {
1469
- // Handle default values
1470
- const finalValue = propValue !== undefined
1471
- ? propValue
1472
- : this.evaluate(prop.value.right, env);
1473
- env.define(prop.value.left.name, finalValue, isConst);
1474
- } else if (prop.value.type === 'ObjectPattern') {
1475
- this.bindObjectPattern(prop.value, propValue, env, isConst);
1476
- } else if (prop.value.type === 'ArrayPattern') {
1477
- this.bindArrayPattern(prop.value, propValue, env, isConst);
1478
- }
1479
- }
1480
- }
1481
- }
1482
-
1483
- bindArrayPattern(pattern, value, env, isConst = false) {
1484
- if (!Array.isArray(value)) {
1485
- throw new TypeError('Cannot destructure non-iterable');
1486
- }
1487
-
1488
- for (let i = 0; i < pattern.elements.length; i++) {
1489
- const element = pattern.elements[i];
1490
- if (!element) continue; // Hole in pattern [a, , c]
1491
-
1492
- if (element.type === 'RestElement') {
1493
- // Handle rest elements [...rest]
1494
- const restValues = value.slice(i);
1495
- env.define(element.argument.name, restValues, isConst);
1496
- break;
1497
- } else if (element.type === 'Identifier') {
1498
- env.define(element.name, value[i], isConst);
1499
- } else if (element.type === 'AssignmentPattern') {
1500
- // Handle default values
1501
- const finalValue = value[i] !== undefined
1502
- ? value[i]
1503
- : this.evaluate(element.right, env);
1504
- env.define(element.left.name, finalValue, isConst);
1505
- } else if (element.type === 'ObjectPattern') {
1506
- this.bindObjectPattern(element, value[i], env, isConst);
1507
- } else if (element.type === 'ArrayPattern') {
1508
- this.bindArrayPattern(element, value[i], env, isConst);
1509
- }
1510
- }
1511
- }
1512
-
1513
- evaluateFunctionDeclaration(node, env) {
1514
- const funcMetadata = {
1515
- __isFunction: true,
1516
- params: node.params,
1517
- body: node.body,
1518
- closure: env,
1519
- expression: false,
1520
- async: node.async || false
1521
- };
1522
-
1523
- // Wrap in actual JavaScript function so it can be called by native code
1524
- const interpreter = this;
1525
-
1526
- // Create async or sync wrapper based on function type
1527
- // Note: using regular function (not arrow) to capture 'this' for method calls
1528
- const wrappedFunc = funcMetadata.async
1529
- ? async function(...args) {
1530
- return await interpreter.callUserFunction(funcMetadata, args, funcMetadata.closure, this);
1531
- }
1532
- : function(...args) {
1533
- return interpreter.callUserFunction(funcMetadata, args, funcMetadata.closure, this);
1534
- };
1535
-
1536
- // Preserve metadata for JSLike's internal use
1537
- wrappedFunc.__isFunction = true;
1538
- wrappedFunc.__metadata = funcMetadata;
1539
-
1540
- env.define(node.id.name, wrappedFunc);
1541
- return undefined;
1542
- }
1543
-
1544
- async evaluateImportDeclaration(node, env) {
1545
- // Get module path from import source
1546
- const modulePath = node.source.value;
1547
-
1548
- // Check if module resolver is configured
1549
- if (!this.moduleResolver) {
1550
- throw new Error('Module resolver not configured - cannot import modules');
1551
- }
1552
-
1553
- // Check if module is already cached
1554
- let moduleExports;
1555
- if (this.moduleCache.has(modulePath)) {
1556
- moduleExports = this.moduleCache.get(modulePath);
1557
- } else {
1558
- // Resolve and load module code
1559
- const resolution = await this.moduleResolver.resolve(modulePath);
1560
- if (!resolution) {
1561
- throw new Error(`Cannot find module '${modulePath}'`);
1562
- }
1563
-
1564
- // Handle both old (string) and new (ModuleResolution) formats
1565
- const moduleCode = typeof resolution === 'string' ? resolution : resolution.code;
1566
-
1567
- // Parse and execute module in its own environment
1568
- const moduleAst = acornParse(moduleCode, {
1569
- ecmaVersion: 2020,
1570
- sourceType: 'module',
1571
- locations: false
1572
- });
1573
- const moduleEnv = new Environment(this.globalEnv);
1574
-
1575
- // Create a new interpreter for the module with shared module cache
1576
- const moduleInterpreter = new Interpreter(this.globalEnv, {
1577
- moduleResolver: this.moduleResolver
1578
- });
1579
- moduleInterpreter.moduleCache = this.moduleCache; // Share cache
1580
-
1581
- // Execute module and collect exports
1582
- await moduleInterpreter.evaluateAsync(moduleAst, moduleEnv);
1583
-
1584
- // Cache the module exports
1585
- moduleExports = moduleInterpreter.moduleExports;
1586
- this.moduleCache.set(modulePath, moduleExports);
1587
- }
1588
-
1589
- // Import specified bindings into current environment
1590
- for (const specifier of node.specifiers) {
1591
- if (specifier.type === 'ImportSpecifier') {
1592
- // Named import: import { foo, bar } from "module"
1593
- const importedName = specifier.imported.name;
1594
- const localName = specifier.local.name;
1595
-
1596
- if (!(importedName in moduleExports)) {
1597
- throw new Error(`Module '${modulePath}' has no export '${importedName}'`);
1598
- }
1599
-
1600
- env.define(localName, moduleExports[importedName]);
1601
- } else if (specifier.type === 'ImportDefaultSpecifier') {
1602
- // Default import: import foo from "module"
1603
- const localName = specifier.local.name;
1604
-
1605
- if (!('default' in moduleExports)) {
1606
- throw new Error(`Module '${modulePath}' has no default export`);
1607
- }
1608
-
1609
- env.define(localName, moduleExports.default);
1610
- } else if (specifier.type === 'ImportNamespaceSpecifier') {
1611
- // Namespace import: import * as foo from "module"
1612
- const localName = specifier.local.name;
1613
- env.define(localName, moduleExports);
1614
- }
1615
- }
1616
-
1617
- return undefined;
1618
- }
1619
-
1620
- evaluateExportNamedDeclaration(node, env) {
1621
- // Handle export with declaration: export function foo() {} or export const x = 42
1622
- if (node.declaration) {
1623
- const result = this.evaluate(node.declaration, env);
1624
-
1625
- // Register exported names
1626
- if (node.declaration.type === 'FunctionDeclaration') {
1627
- // export function foo() {}
1628
- const name = node.declaration.id.name;
1629
- this.moduleExports[name] = env.get(name);
1630
- } else if (node.declaration.type === 'VariableDeclaration') {
1631
- // export const x = 42, y = 10
1632
- for (const declarator of node.declaration.declarations) {
1633
- const name = declarator.id.name;
1634
- this.moduleExports[name] = env.get(name);
1635
- }
1636
- } else if (node.declaration.type === 'ClassDeclaration') {
1637
- // export class Foo {}
1638
- const name = node.declaration.id.name;
1639
- this.moduleExports[name] = env.get(name);
1640
- }
1641
-
1642
- return result;
1643
- }
1644
-
1645
- // Handle export list: export { foo, bar }
1646
- if (node.specifiers && node.specifiers.length > 0) {
1647
- for (const specifier of node.specifiers) {
1648
- const exportedName = specifier.exported.name;
1649
- const localName = specifier.local.name;
1650
- this.moduleExports[exportedName] = env.get(localName);
1651
- }
1652
- }
1653
-
1654
- return undefined;
1655
- }
1656
-
1657
- evaluateExportDefaultDeclaration(node, env) {
1658
- // Evaluate the default export expression/declaration
1659
- let value;
1660
-
1661
- if (node.declaration.type === 'FunctionDeclaration' || node.declaration.type === 'ClassDeclaration') {
1662
- // export default function foo() {} or export default class Foo {}
1663
- value = this.evaluate(node.declaration, env);
1664
- // If it has a name, it's also defined in the environment
1665
- if (node.declaration.id) {
1666
- value = env.get(node.declaration.id.name);
1667
- }
1668
- } else {
1669
- // export default expression
1670
- value = this.evaluate(node.declaration, env);
1671
- }
1672
-
1673
- // Register as default export
1674
- this.moduleExports.default = value;
1675
-
1676
- return undefined;
1677
- }
1678
-
1679
- evaluateBlockStatement(node, env) {
1680
- const blockEnv = new Environment(env);
1681
- let result;
1682
-
1683
- for (const statement of node.body) {
1684
- result = this.evaluate(statement, blockEnv);
1685
- if (result instanceof ReturnValue ||
1686
- result instanceof BreakSignal ||
1687
- result instanceof ContinueSignal ||
1688
- result instanceof ThrowSignal) {
1689
- return result;
1690
- }
1691
- }
1692
-
1693
- return result;
1694
- }
1695
-
1696
- evaluateIfStatement(node, env) {
1697
- const test = this.evaluate(node.test, env);
1698
-
1699
- if (test) {
1700
- return this.evaluate(node.consequent, env);
1701
- } else if (node.alternate) {
1702
- return this.evaluate(node.alternate, env);
1703
- }
1704
-
1705
- return undefined;
1706
- }
1707
-
1708
- evaluateWhileStatement(node, env) {
1709
- let result;
1710
-
1711
- while (this.evaluate(node.test, env)) {
1712
- result = this.evaluate(node.body, env);
1713
-
1714
- if (result instanceof BreakSignal) {
1715
- break;
1716
- }
1717
- if (result instanceof ContinueSignal) {
1718
- continue;
1719
- }
1720
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
1721
- return result;
1722
- }
1723
- }
1724
-
1725
- return undefined;
1726
- }
1727
-
1728
- evaluateDoWhileStatement(node, env) {
1729
- let result;
1730
-
1731
- do {
1732
- result = this.evaluate(node.body, env);
1733
-
1734
- if (result instanceof BreakSignal) {
1735
- break;
1736
- }
1737
- if (result instanceof ContinueSignal) {
1738
- continue;
1739
- }
1740
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
1741
- return result;
1742
- }
1743
- } while (this.evaluate(node.test, env));
1744
-
1745
- return undefined;
1746
- }
1747
-
1748
- evaluateForStatement(node, env) {
1749
- const forEnv = new Environment(env);
1750
- let result;
1751
-
1752
- if (node.init) {
1753
- this.evaluate(node.init, forEnv);
1754
- }
1755
-
1756
- while (!node.test || this.evaluate(node.test, forEnv)) {
1757
- result = this.evaluate(node.body, forEnv);
1758
-
1759
- if (result instanceof BreakSignal) {
1760
- break;
1761
- }
1762
- if (result instanceof ContinueSignal) {
1763
- if (node.update) {
1764
- this.evaluate(node.update, forEnv);
1765
- }
1766
- continue;
1767
- }
1768
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
1769
- return result;
1770
- }
1771
-
1772
- if (node.update) {
1773
- this.evaluate(node.update, forEnv);
1774
- }
1775
- }
1776
-
1777
- return undefined;
1778
- }
1779
-
1780
- evaluateForInStatement(node, env) {
1781
- const forEnv = new Environment(env);
1782
- const obj = this.evaluate(node.right, forEnv);
1783
- let result;
1784
-
1785
- // Check for null or undefined - JavaScript throws TypeError
1786
- if (obj === null || obj === undefined) {
1787
- throw new TypeError(`Cannot use 'in' operator to iterate over ${obj}`);
1788
- }
1789
-
1790
- // Get the variable name from the declaration
1791
- const varName = node.left.declarations[0].id.name;
1792
-
1793
- // Define the variable once before the loop
1794
- forEnv.define(varName, undefined);
1795
-
1796
- for (const key in obj) {
1797
- // Update the variable value for each iteration
1798
- forEnv.set(varName, key);
1799
- result = this.evaluate(node.body, forEnv);
1800
-
1801
- if (result instanceof BreakSignal) {
1802
- break;
1803
- }
1804
- if (result instanceof ContinueSignal) {
1805
- continue;
1806
- }
1807
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
1808
- return result;
1809
- }
1810
- }
1811
-
1812
- return undefined;
1813
- }
1814
-
1815
- evaluateForOfStatement(node, env) {
1816
- const forEnv = new Environment(env);
1817
- const iterable = this.evaluate(node.right, forEnv);
1818
- let result;
1819
-
1820
- const declarator = node.left.declarations[0];
1821
- const isConst = node.left.kind === 'const';
1822
-
1823
- for (const value of iterable) {
1824
- // Create a new child environment for each iteration to handle const properly
1825
- const iterEnv = forEnv.extend();
1826
-
1827
- // Bind the value using the appropriate pattern
1828
- if (declarator.id.type === 'Identifier') {
1829
- iterEnv.define(declarator.id.name, value, isConst);
1830
- } else if (declarator.id.type === 'ArrayPattern') {
1831
- this.bindArrayPattern(declarator.id, value, iterEnv, isConst);
1832
- } else if (declarator.id.type === 'ObjectPattern') {
1833
- this.bindObjectPattern(declarator.id, value, iterEnv, isConst);
1834
- }
1835
-
1836
- result = this.evaluate(node.body, iterEnv);
1837
-
1838
- if (result instanceof BreakSignal) {
1839
- break;
1840
- }
1841
- if (result instanceof ContinueSignal) {
1842
- continue;
1843
- }
1844
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
1845
- return result;
1846
- }
1847
- }
1848
-
1849
- return undefined;
1850
- }
1851
-
1852
- evaluateTryStatement(node, env) {
1853
- let result;
1854
-
1855
- try {
1856
- result = this.evaluate(node.block, env);
1857
-
1858
- if (result instanceof ThrowSignal) {
1859
- throw result.value;
1860
- }
1861
- } catch (error) {
1862
- if (node.handler) {
1863
- const catchEnv = new Environment(env);
1864
- if (node.handler.param) {
1865
- catchEnv.define(node.handler.param.name, error);
1866
- }
1867
- result = this.evaluate(node.handler.body, catchEnv);
1868
- } else {
1869
- throw error;
1870
- }
1871
- } finally {
1872
- if (node.finalizer) {
1873
- const finalResult = this.evaluate(node.finalizer, env);
1874
- // If finally block throws or returns, it overrides the try/catch result
1875
- if (finalResult instanceof ThrowSignal || finalResult instanceof ReturnValue) {
1876
- return finalResult;
1877
- }
1878
- }
1879
- }
1880
-
1881
- return result;
1882
- }
1883
-
1884
- evaluateSwitchStatement(node, env) {
1885
- const discriminant = this.evaluate(node.discriminant, env);
1886
- let matched = false;
1887
- let result;
1888
-
1889
- for (const switchCase of node.cases) {
1890
- // Check if this case matches (or if we're in fall-through mode)
1891
- if (!matched && switchCase.test) {
1892
- const testValue = this.evaluate(switchCase.test, env);
1893
- if (testValue === discriminant) {
1894
- matched = true;
1895
- }
1896
- } else if (!switchCase.test) {
1897
- // Default case
1898
- matched = true;
1899
- }
1900
-
1901
- // Execute consequent if matched
1902
- if (matched) {
1903
- for (const statement of switchCase.consequent) {
1904
- result = this.evaluate(statement, env);
1905
-
1906
- if (result instanceof BreakSignal) {
1907
- return undefined;
1908
- }
1909
- if (result instanceof ReturnValue || result instanceof ThrowSignal) {
1910
- return result;
1911
- }
1912
- }
1913
- }
1914
- }
1915
-
1916
- return undefined;
1917
- }
1918
-
1919
- // ===== ES6+ Feature Implementations =====
1920
-
1921
- evaluateTemplateLiteral(node, env) {
1922
- let result = '';
1923
- for (let i = 0; i < node.quasis.length; i++) {
1924
- result += node.quasis[i].value.cooked || node.quasis[i].value.raw;
1925
- if (i < node.expressions.length) {
1926
- const exprValue = this.evaluate(node.expressions[i], env);
1927
- result += String(exprValue);
1928
- }
1929
- }
1930
- return result;
1931
- }
1932
-
1933
- evaluateClassDeclaration(node, env) {
1934
- const className = node.id.name;
1935
- const classFunc = this.createClass(node, env);
1936
- env.define(className, classFunc);
1937
- return undefined;
1938
- }
1939
-
1940
- evaluateClassExpression(node, env) {
1941
- return this.createClass(node, env);
1942
- }
1943
-
1944
- createClass(node, env) {
1945
- const className = node.id ? node.id.name : 'AnonymousClass';
1946
- const superClass = node.superClass ? this.evaluate(node.superClass, env) : null;
1947
- const interpreter = this; // Capture interpreter reference
1948
-
1949
- // Find constructor
1950
- let constructor = null;
1951
- const methods = {};
1952
- const staticMethods = {};
1953
-
1954
- for (const member of node.body.body) {
1955
- if (member.type === 'MethodDefinition') {
1956
- const methodName = member.key.name || member.key.value;
1957
- const methodFunc = this.createMethodFunction(member.value, env, className);
1958
-
1959
- if (member.kind === 'constructor') {
1960
- constructor = methodFunc;
1961
- } else if (member.static) {
1962
- staticMethods[methodName] = methodFunc;
1963
- } else {
1964
- methods[methodName] = methodFunc;
1965
- }
1966
- }
1967
- }
1968
-
1969
- // Create class constructor function
1970
- const classConstructor = function(...args) {
1971
- // Create instance
1972
- const instance = Object.create(classConstructor.prototype);
1973
-
1974
- // Call constructor - super() must be called explicitly inside constructor
1975
- if (constructor) {
1976
- const result = interpreter.callMethodFunction(constructor, instance, args, env, superClass);
1977
- // Only use the returned object if it's an explicit return of an object (not the instance)
1978
- if (result && result.__explicitReturn && result.value && typeof result.value === 'object' && result.value !== instance) {
1979
- return result.value;
1980
- }
1981
- } else if (superClass) {
1982
- // If no constructor defined but has superClass, implicitly call super()
1983
- // Call the superClass constructor properly - it's a classConstructor function
1984
- superClass.call(instance, ...args);
1985
- }
1986
-
1987
- return instance;
1988
- };
1989
-
1990
- // Store the constructor method on the classConstructor for super() to access
1991
- if (constructor) {
1992
- classConstructor.__constructor = constructor;
1993
- }
1994
-
1995
- // Set up prototype chain
1996
- if (superClass) {
1997
- classConstructor.prototype = Object.create(superClass.prototype);
1998
- classConstructor.prototype.constructor = classConstructor;
1999
- }
2000
-
2001
- // Add methods to prototype
2002
- for (const [name, method] of Object.entries(methods)) {
2003
- classConstructor.prototype[name] = function(...args) {
2004
- const result = interpreter.callMethodFunction(method, this, args, env);
2005
- // Unwrap explicit return marker
2006
- if (result && result.__explicitReturn) {
2007
- return result.value;
2008
- }
2009
- return result;
2010
- };
2011
- }
2012
-
2013
- // Add static methods
2014
- for (const [name, method] of Object.entries(staticMethods)) {
2015
- classConstructor[name] = function(...args) {
2016
- const result = interpreter.callMethodFunction(method, classConstructor, args, env);
2017
- // Unwrap explicit return marker
2018
- if (result && result.__explicitReturn) {
2019
- return result.value;
2020
- }
2021
- return result;
2022
- };
2023
- }
2024
-
2025
- classConstructor.__className = className;
2026
- return classConstructor;
2027
- }
2028
-
2029
- createMethodFunction(funcNode, env, className) {
2030
- const func = {
2031
- __isFunction: true,
2032
- __params: funcNode.params,
2033
- __body: funcNode.body,
2034
- __env: env,
2035
- __className: className
2036
- };
2037
- return func;
2038
- }
2039
-
2040
- callMethodFunction(methodFunc, thisContext, args, env, superClass = null) {
2041
- const funcEnv = new Environment(methodFunc.__env || env);
2042
-
2043
- // Bind 'this'
2044
- funcEnv.define('this', thisContext);
2045
-
2046
- // Bind 'super' if superClass exists
2047
- if (superClass) {
2048
- // Create a super function that calls the parent constructor
2049
- const superFunc = (...superArgs) => {
2050
- // Call the parent constructor method if it exists
2051
- if (superClass.__constructor) {
2052
- this.callMethodFunction(superClass.__constructor, thisContext, superArgs, env, null);
2053
- }
2054
- // Otherwise, call superClass as a regular constructor (for native/external classes)
2055
- else {
2056
- // For native constructors like Error, we need to use Reflect.construct
2057
- // to properly initialize the instance properties
2058
- const tempInstance = Reflect.construct(superClass, superArgs, thisContext.constructor);
2059
- // Copy properties from the temp instance to our thisContext
2060
- Object.getOwnPropertyNames(tempInstance).forEach(name => {
2061
- thisContext[name] = tempInstance[name];
2062
- });
2063
- }
2064
- return undefined;
2065
- };
2066
- // Store both the function and mark it as super
2067
- superFunc.__isSuperConstructor = true;
2068
- superFunc.__superClass = superClass;
2069
- funcEnv.define('super', superFunc);
2070
- }
2071
-
2072
- // Bind parameters
2073
- for (let i = 0; i < methodFunc.__params.length; i++) {
2074
- const param = methodFunc.__params[i];
2075
-
2076
- if (param.type === 'Identifier') {
2077
- // Simple parameter: function(x)
2078
- funcEnv.define(param.name, args[i]);
2079
- } else if (param.type === 'AssignmentPattern') {
2080
- // Default parameter: function(x = defaultValue)
2081
- const value = args[i] !== undefined ? args[i] : this.evaluate(param.right, funcEnv);
2082
- funcEnv.define(param.left.name, value);
2083
- } else if (param.type === 'RestElement') {
2084
- // Rest parameter: function(...rest)
2085
- funcEnv.define(param.argument.name, args.slice(i));
2086
- break; // Rest element must be last
2087
- } else if (param.type === 'ObjectPattern') {
2088
- // Destructuring parameter: function({a, b})
2089
- this.bindObjectPattern(param, args[i], funcEnv);
2090
- } else if (param.type === 'ArrayPattern') {
2091
- // Array destructuring parameter: function([a, b])
2092
- this.bindArrayPattern(param, args[i], funcEnv);
2093
- } else {
2094
- // Fallback for simple parameter names
2095
- funcEnv.define(param.name, args[i]);
2096
- }
2097
- }
2098
-
2099
- const result = this.evaluate(methodFunc.__body, funcEnv);
2100
-
2101
- if (result instanceof ReturnValue) {
2102
- // Mark that this was an explicit return for constructor handling
2103
- return { __explicitReturn: true, value: result.value };
2104
- }
2105
-
2106
- // If the result is a ThrowSignal, throw the error
2107
- if (result instanceof ThrowSignal) {
2108
- throw result.value;
2109
- }
2110
-
2111
- // Return implicit result (for arrow function expressions)
2112
- return result;
2113
- }
2114
-
2115
- evaluateMethodDefinition(node, env) {
2116
- // This is handled by class creation
2117
- return undefined;
2118
- }
2119
-
2120
- evaluateSpreadElement(node, env) {
2121
- const arg = this.evaluate(node.argument, env);
2122
- if (Array.isArray(arg)) {
2123
- return { __spread: true, __values: arg };
2124
- }
2125
- if (typeof arg === 'object' && arg !== null) {
2126
- return { __spread: true, __values: Object.entries(arg) };
2127
- }
2128
- throw new TypeError('Spread syntax requires an iterable');
2129
- }
2130
-
2131
- evaluateRestElement(node, env) {
2132
- // Handled during parameter binding
2133
- return undefined;
2134
- }
2135
-
2136
- evaluateObjectPattern(node, env) {
2137
- // Handled during destructuring
2138
- return undefined;
2139
- }
2140
-
2141
- evaluateArrayPattern(node, env) {
2142
- // Handled during destructuring
2143
- return undefined;
2144
- }
2145
-
2146
- evaluateAssignmentPattern(node, env) {
2147
- // Handled during parameter binding with defaults
2148
- return undefined;
2149
- }
2150
-
2151
- evaluateProperty(node, env) {
2152
- // Already handled in evaluateObjectExpression
2153
- return undefined;
2154
- }
2155
- }