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.
- package/bin/jslike.js +1 -1
- package/dist/editor/monaco/index.cjs +239 -0
- package/{src/editor/monaco/index.js → dist/editor/monaco/index.d.cts} +4 -2
- package/dist/editor/monaco/index.d.ts +236 -0
- package/dist/editor/monaco/index.js +214 -0
- package/dist/editor/wang-prism.cjs +109 -0
- package/dist/editor/wang-prism.d.ts +136 -0
- package/dist/editor/wang-prism.js +109 -0
- package/dist/index.cjs +8106 -0
- package/{src/parser.js → dist/index.d.cts} +3276 -41
- package/dist/index.d.ts +9476 -0
- package/dist/index.js +8072 -0
- package/dist/validator/index.cjs +5753 -0
- package/dist/validator/index.d.cts +103 -0
- package/{src/validator/index.js → dist/validator/index.d.ts} +5 -2
- package/dist/validator/index.js +5727 -0
- package/package.json +49 -10
- package/src/ast/nodes.js +0 -236
- package/src/cli/wang-run.js +0 -89
- package/src/cli/wang-validate.js +0 -87
- package/src/errors/enhanced-error.js +0 -124
- package/src/index.js +0 -186
- package/src/interpreter/index.js +0 -201
- package/src/interpreter/interpreter.js +0 -2155
- package/src/metadata/index.js +0 -531
- package/src/resolvers/memory.js +0 -17
- package/src/runtime/builtins.js +0 -517
- package/src/runtime/environment.js +0 -75
- /package/{src/editor/wang-prism.js → dist/editor/wang-prism.d.cts} +0 -0
|
@@ -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
|
-
}
|