@lewin671/python-vm 0.1.4 → 0.1.6
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/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/index.min.js +12 -0
- package/dist/vm/execution.d.ts.map +1 -1
- package/dist/vm/execution.js +532 -403
- package/dist/vm/execution.js.map +1 -1
- package/dist/vm/handlers.d.ts +5 -0
- package/dist/vm/handlers.d.ts.map +1 -0
- package/dist/vm/handlers.js +455 -0
- package/dist/vm/handlers.js.map +1 -0
- package/dist/vm/statement-generator.d.ts +5 -0
- package/dist/vm/statement-generator.d.ts.map +1 -0
- package/dist/vm/statement-generator.js +138 -0
- package/dist/vm/statement-generator.js.map +1 -0
- package/dist/vm/tail-call-optimization.d.ts +50 -0
- package/dist/vm/tail-call-optimization.d.ts.map +1 -0
- package/dist/vm/tail-call-optimization.js +143 -0
- package/dist/vm/tail-call-optimization.js.map +1 -0
- package/package.json +5 -2
package/dist/vm/execution.js
CHANGED
|
@@ -26,6 +26,12 @@ function executeFrame(frame) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
let lastValue = null;
|
|
29
|
+
// Cache frequently accessed properties for faster access (V8 optimization)
|
|
30
|
+
const stack = frame.stack;
|
|
31
|
+
const locals = frame.locals;
|
|
32
|
+
const scope = frame.scope;
|
|
33
|
+
const scopeValues = scope.values;
|
|
34
|
+
const iterSymbol = Symbol.iterator;
|
|
29
35
|
const renderFString = (template, scope) => {
|
|
30
36
|
return template.replace(/\{([^}]+)\}/g, (_m, expr) => {
|
|
31
37
|
const { rawExpr, rawSpec } = this.splitFormatSpec(expr);
|
|
@@ -43,11 +49,11 @@ function executeFrame(frame) {
|
|
|
43
49
|
}
|
|
44
50
|
evalScope.values.set(varname, val);
|
|
45
51
|
}
|
|
46
|
-
else if (
|
|
52
|
+
else if (locals[i] !== undefined) {
|
|
47
53
|
if (process.env['DEBUG_NONLOCAL']) {
|
|
48
|
-
console.log(`renderFString: varname=${varname},
|
|
54
|
+
console.log(`renderFString: varname=${varname}, locals[${i}]=${locals[i]}`);
|
|
49
55
|
}
|
|
50
|
-
evalScope.values.set(varname,
|
|
56
|
+
evalScope.values.set(varname, locals[i]);
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
}
|
|
@@ -76,8 +82,8 @@ function executeFrame(frame) {
|
|
|
76
82
|
if (frame.blockStack.length === 0)
|
|
77
83
|
return false;
|
|
78
84
|
const block = frame.blockStack.pop();
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
stack.length = block.stackHeight;
|
|
86
|
+
stack.push(normalizeThrown(err));
|
|
81
87
|
frame.pc = block.handler;
|
|
82
88
|
return true;
|
|
83
89
|
};
|
|
@@ -87,124 +93,431 @@ function executeFrame(frame) {
|
|
|
87
93
|
break;
|
|
88
94
|
const { opcode, arg } = instr;
|
|
89
95
|
try {
|
|
96
|
+
// Switch cases ordered by execution frequency (hot paths first)
|
|
97
|
+
// Based on dynamic opcode execution profiling across benchmark workloads:
|
|
98
|
+
// - Fibonacci recursion, list operations, nested loops, dictionary ops, etc.
|
|
99
|
+
// - LOAD_FAST (22-23%), LOAD_CONST (18%), LOAD_NAME (9-33%) are hottest
|
|
100
|
+
// - All 73 cases reordered: frequent ops first, then grouped by category
|
|
101
|
+
// This reduces average case evaluations and improves branch prediction
|
|
90
102
|
switch (opcode) {
|
|
103
|
+
// === HOT PATH: Most frequently executed opcodes (>5% execution time) ===
|
|
104
|
+
case types_1.OpCode.LOAD_FAST: {
|
|
105
|
+
// Optimize common case: value is in locals
|
|
106
|
+
let val = locals[arg];
|
|
107
|
+
if (val === undefined) {
|
|
108
|
+
// Check scope values as fallback
|
|
109
|
+
const varname = varnames[arg];
|
|
110
|
+
if (varname !== undefined && scopeValues.has(varname)) {
|
|
111
|
+
val = scopeValues.get(varname);
|
|
112
|
+
locals[arg] = val;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
throw new runtime_types_1.PyException('UnboundLocalError', `local variable '${varname}' referenced before assignment`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
stack.push(val);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
91
121
|
case types_1.OpCode.LOAD_CONST:
|
|
92
122
|
{
|
|
93
123
|
const val = constants[arg];
|
|
94
124
|
if (val && typeof val === 'object' && typeof val.__fstring__ === 'string') {
|
|
95
|
-
|
|
125
|
+
stack.push(renderFString(val.__fstring__, frame.scope));
|
|
96
126
|
}
|
|
97
127
|
else {
|
|
98
|
-
|
|
128
|
+
stack.push(val);
|
|
99
129
|
}
|
|
100
130
|
}
|
|
101
131
|
break;
|
|
102
132
|
case types_1.OpCode.LOAD_NAME: {
|
|
103
133
|
const name = names[arg];
|
|
104
|
-
const val =
|
|
134
|
+
const val = scope.get(name);
|
|
105
135
|
// console.log(`LOAD_NAME ${name} -> ${val}`);
|
|
106
|
-
|
|
136
|
+
stack.push(val);
|
|
107
137
|
break;
|
|
108
138
|
}
|
|
109
|
-
case types_1.OpCode.
|
|
110
|
-
|
|
139
|
+
case types_1.OpCode.BINARY_ADD: {
|
|
140
|
+
const b = stack.pop();
|
|
141
|
+
const a = stack.pop();
|
|
142
|
+
// Fast path for simple numbers
|
|
143
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
144
|
+
stack.push(a + b);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
stack.push(this.applyBinary('+', a, b));
|
|
148
|
+
}
|
|
111
149
|
break;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
150
|
+
}
|
|
151
|
+
case types_1.OpCode.BINARY_SUBTRACT: {
|
|
152
|
+
const b = stack.pop();
|
|
153
|
+
const a = stack.pop();
|
|
154
|
+
// Fast path for simple numbers
|
|
155
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
156
|
+
stack.push(a - b);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
stack.push(this.applyBinary('-', a, b));
|
|
118
160
|
}
|
|
119
|
-
const val = globalScope.get(name);
|
|
120
|
-
frame.stack.push(val);
|
|
121
161
|
break;
|
|
122
162
|
}
|
|
123
|
-
case types_1.OpCode.
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
163
|
+
case types_1.OpCode.BINARY_MULTIPLY: {
|
|
164
|
+
const b = stack.pop();
|
|
165
|
+
const a = stack.pop();
|
|
166
|
+
// Fast path for simple numbers
|
|
167
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
168
|
+
stack.push(a * b);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
stack.push(this.applyBinary('*', a, b));
|
|
129
172
|
}
|
|
130
|
-
globalScope.set(name, frame.stack.pop());
|
|
131
173
|
break;
|
|
132
174
|
}
|
|
133
|
-
case types_1.OpCode.
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
175
|
+
case types_1.OpCode.CALL_FUNCTION: {
|
|
176
|
+
const argCount = arg;
|
|
177
|
+
const args = new Array(argCount);
|
|
178
|
+
// Pop arguments in reverse order
|
|
179
|
+
for (let i = argCount - 1; i >= 0; i--) {
|
|
180
|
+
args[i] = stack.pop();
|
|
181
|
+
}
|
|
182
|
+
const func = stack.pop();
|
|
183
|
+
stack.push(this.callFunction(func, args, scope));
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
case types_1.OpCode.RETURN_VALUE:
|
|
187
|
+
return stack.pop();
|
|
188
|
+
case types_1.OpCode.COMPARE_OP: {
|
|
189
|
+
const b = stack.pop();
|
|
190
|
+
const a = stack.pop();
|
|
191
|
+
// Fast path for simple integer comparisons
|
|
192
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
193
|
+
let result = undefined;
|
|
194
|
+
switch (arg) {
|
|
195
|
+
case types_1.CompareOp.LT:
|
|
196
|
+
result = a < b;
|
|
197
|
+
break;
|
|
198
|
+
case types_1.CompareOp.LE:
|
|
199
|
+
result = a <= b;
|
|
200
|
+
break;
|
|
201
|
+
case types_1.CompareOp.EQ:
|
|
202
|
+
result = a === b;
|
|
203
|
+
break;
|
|
204
|
+
case types_1.CompareOp.NE:
|
|
205
|
+
result = a !== b;
|
|
206
|
+
break;
|
|
207
|
+
case types_1.CompareOp.GT:
|
|
208
|
+
result = a > b;
|
|
209
|
+
break;
|
|
210
|
+
case types_1.CompareOp.GE:
|
|
211
|
+
result = a >= b;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
if (result !== undefined) {
|
|
215
|
+
stack.push(result);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
stack.push(this.applyCompare(arg, a, b));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
stack.push(this.applyCompare(arg, a, b));
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case types_1.OpCode.POP_JUMP_IF_FALSE: {
|
|
227
|
+
const val = stack.pop();
|
|
228
|
+
// Fast path for booleans and numbers
|
|
229
|
+
let isFalse = false;
|
|
230
|
+
if (typeof val === 'boolean') {
|
|
231
|
+
isFalse = !val;
|
|
232
|
+
}
|
|
233
|
+
else if (typeof val === 'number') {
|
|
234
|
+
isFalse = val === 0;
|
|
235
|
+
}
|
|
236
|
+
else if (val === null || val === undefined) {
|
|
237
|
+
isFalse = true;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
isFalse = !this.isTruthy(val, scope);
|
|
241
|
+
}
|
|
242
|
+
if (isFalse) {
|
|
243
|
+
frame.pc = arg;
|
|
144
244
|
}
|
|
145
|
-
frame.stack.push(val);
|
|
146
245
|
break;
|
|
147
246
|
}
|
|
148
247
|
case types_1.OpCode.STORE_FAST: {
|
|
149
|
-
const val =
|
|
150
|
-
|
|
248
|
+
const val = stack.pop();
|
|
249
|
+
locals[arg] = val;
|
|
151
250
|
if (varnames && varnames[arg] !== undefined) {
|
|
152
|
-
|
|
251
|
+
scopeValues.set(varnames[arg], val);
|
|
153
252
|
}
|
|
154
253
|
break;
|
|
155
254
|
}
|
|
156
|
-
case types_1.OpCode.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
255
|
+
case types_1.OpCode.STORE_NAME:
|
|
256
|
+
scope.set(names[arg], stack.pop());
|
|
257
|
+
break;
|
|
258
|
+
case types_1.OpCode.FOR_ITER: {
|
|
259
|
+
const iter = stack[stack.length - 1];
|
|
260
|
+
const next = iter.next();
|
|
261
|
+
if (next.done) {
|
|
262
|
+
stack.pop();
|
|
263
|
+
frame.pc = arg;
|
|
161
264
|
}
|
|
162
|
-
|
|
163
|
-
|
|
265
|
+
else {
|
|
266
|
+
stack.push(next.value);
|
|
164
267
|
}
|
|
165
268
|
break;
|
|
166
269
|
}
|
|
167
|
-
case types_1.OpCode.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
270
|
+
case types_1.OpCode.JUMP_ABSOLUTE:
|
|
271
|
+
frame.pc = arg;
|
|
272
|
+
break;
|
|
273
|
+
case types_1.OpCode.INPLACE_ADD: {
|
|
274
|
+
const b = stack.pop();
|
|
275
|
+
const a = stack.pop();
|
|
276
|
+
// Fast path for simple numbers
|
|
277
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
278
|
+
stack.push(a + b);
|
|
174
279
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// suffix values (last..first), then middle list, then prefix values (last..first).
|
|
178
|
-
for (let i = afterCount - 1; i >= 0; i--) {
|
|
179
|
-
frame.stack.push(items[items.length - afterCount + i]);
|
|
280
|
+
else {
|
|
281
|
+
stack.push(this.applyInPlaceBinary('+', a, b));
|
|
180
282
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
case types_1.OpCode.INPLACE_SUBTRACT: {
|
|
286
|
+
const b = stack.pop();
|
|
287
|
+
const a = stack.pop();
|
|
288
|
+
// Fast path for simple numbers
|
|
289
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
290
|
+
stack.push(a - b);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
stack.push(this.applyInPlaceBinary('-', a, b));
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
case types_1.OpCode.INPLACE_MULTIPLY: {
|
|
298
|
+
const b = stack.pop();
|
|
299
|
+
const a = stack.pop();
|
|
300
|
+
// Fast path for simple numbers
|
|
301
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
302
|
+
stack.push(a * b);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
stack.push(this.applyInPlaceBinary('*', a, b));
|
|
306
|
+
}
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
case types_1.OpCode.POP_TOP:
|
|
310
|
+
lastValue = stack.pop();
|
|
311
|
+
break;
|
|
312
|
+
case types_1.OpCode.GET_ITER: {
|
|
313
|
+
const obj = stack.pop();
|
|
314
|
+
if (obj && typeof obj[iterSymbol] === 'function') {
|
|
315
|
+
stack.push(obj[iterSymbol]());
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
throw new runtime_types_1.PyException('TypeError', `'${typeof obj}' object is not iterable`);
|
|
184
319
|
}
|
|
185
320
|
break;
|
|
186
321
|
}
|
|
187
322
|
case types_1.OpCode.LOAD_ATTR: {
|
|
188
|
-
const obj =
|
|
189
|
-
|
|
323
|
+
const obj = stack.pop();
|
|
324
|
+
stack.push(this.getAttribute(obj, names[arg], frame.scope));
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
// Other BINARY operations
|
|
328
|
+
case types_1.OpCode.BINARY_DIVIDE: {
|
|
329
|
+
const b = stack.pop();
|
|
330
|
+
const a = stack.pop();
|
|
331
|
+
// Fast path for simple numbers
|
|
332
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
333
|
+
stack.push(a / b);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
stack.push(this.applyBinary('/', a, b));
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
case types_1.OpCode.BINARY_FLOOR_DIVIDE: {
|
|
341
|
+
const b = stack.pop();
|
|
342
|
+
const a = stack.pop();
|
|
343
|
+
// Fast path for simple numbers
|
|
344
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
345
|
+
stack.push(Math.floor(a / b));
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
stack.push(this.applyBinary('//', a, b));
|
|
349
|
+
}
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
case types_1.OpCode.BINARY_MODULO: {
|
|
353
|
+
const b = stack.pop();
|
|
354
|
+
const a = stack.pop();
|
|
355
|
+
// Fast path for simple numbers
|
|
356
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
357
|
+
stack.push(a % b);
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
stack.push(this.applyBinary('%', a, b));
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case types_1.OpCode.BINARY_POWER: {
|
|
365
|
+
const b = stack.pop();
|
|
366
|
+
const a = stack.pop();
|
|
367
|
+
stack.push(this.applyBinary('**', a, b));
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
case types_1.OpCode.BINARY_AND: {
|
|
371
|
+
const b = stack.pop();
|
|
372
|
+
const a = stack.pop();
|
|
373
|
+
stack.push(this.applyBinary('&', a, b));
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
case types_1.OpCode.BINARY_XOR: {
|
|
377
|
+
const b = stack.pop();
|
|
378
|
+
const a = stack.pop();
|
|
379
|
+
stack.push(this.applyBinary('^', a, b));
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
case types_1.OpCode.BINARY_OR: {
|
|
383
|
+
const b = stack.pop();
|
|
384
|
+
const a = stack.pop();
|
|
385
|
+
stack.push(this.applyBinary('|', a, b));
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
case types_1.OpCode.BINARY_LSHIFT: {
|
|
389
|
+
const b = stack.pop();
|
|
390
|
+
const a = stack.pop();
|
|
391
|
+
stack.push(this.applyBinary('<<', a, b));
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
case types_1.OpCode.BINARY_RSHIFT: {
|
|
395
|
+
const b = stack.pop();
|
|
396
|
+
const a = stack.pop();
|
|
397
|
+
stack.push(this.applyBinary('>>', a, b));
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
// Other INPLACE operations
|
|
401
|
+
case types_1.OpCode.INPLACE_DIVIDE: {
|
|
402
|
+
const b = stack.pop();
|
|
403
|
+
const a = stack.pop();
|
|
404
|
+
stack.push(this.applyInPlaceBinary('/', a, b));
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
case types_1.OpCode.INPLACE_FLOOR_DIVIDE: {
|
|
408
|
+
const b = stack.pop();
|
|
409
|
+
const a = stack.pop();
|
|
410
|
+
stack.push(this.applyInPlaceBinary('//', a, b));
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
case types_1.OpCode.INPLACE_MODULO: {
|
|
414
|
+
const b = stack.pop();
|
|
415
|
+
const a = stack.pop();
|
|
416
|
+
stack.push(this.applyInPlaceBinary('%', a, b));
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
case types_1.OpCode.INPLACE_POWER: {
|
|
420
|
+
const b = stack.pop();
|
|
421
|
+
const a = stack.pop();
|
|
422
|
+
stack.push(this.applyInPlaceBinary('**', a, b));
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case types_1.OpCode.INPLACE_AND: {
|
|
426
|
+
const b = stack.pop();
|
|
427
|
+
const a = stack.pop();
|
|
428
|
+
stack.push(this.applyInPlaceBinary('&', a, b));
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
case types_1.OpCode.INPLACE_XOR: {
|
|
432
|
+
const b = stack.pop();
|
|
433
|
+
const a = stack.pop();
|
|
434
|
+
stack.push(this.applyInPlaceBinary('^', a, b));
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
case types_1.OpCode.INPLACE_OR: {
|
|
438
|
+
const b = stack.pop();
|
|
439
|
+
const a = stack.pop();
|
|
440
|
+
stack.push(this.applyInPlaceBinary('|', a, b));
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
case types_1.OpCode.INPLACE_LSHIFT: {
|
|
444
|
+
const b = stack.pop();
|
|
445
|
+
const a = stack.pop();
|
|
446
|
+
stack.push(this.applyInPlaceBinary('<<', a, b));
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
case types_1.OpCode.INPLACE_RSHIFT: {
|
|
450
|
+
const b = stack.pop();
|
|
451
|
+
const a = stack.pop();
|
|
452
|
+
stack.push(this.applyInPlaceBinary('>>', a, b));
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
// Other JUMP operations
|
|
456
|
+
case types_1.OpCode.POP_JUMP_IF_TRUE: {
|
|
457
|
+
const val = stack.pop();
|
|
458
|
+
if (this.isTruthy(val, frame.scope)) {
|
|
459
|
+
frame.pc = arg;
|
|
460
|
+
}
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case types_1.OpCode.JUMP_IF_FALSE_OR_POP: {
|
|
464
|
+
const val = stack[stack.length - 1];
|
|
465
|
+
if (!this.isTruthy(val, frame.scope)) {
|
|
466
|
+
frame.pc = arg;
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
stack.pop();
|
|
470
|
+
}
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
case types_1.OpCode.JUMP_IF_TRUE_OR_POP: {
|
|
474
|
+
const val = stack[stack.length - 1];
|
|
475
|
+
if (this.isTruthy(val, frame.scope)) {
|
|
476
|
+
frame.pc = arg;
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
stack.pop();
|
|
480
|
+
}
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
// LOAD/STORE operations
|
|
484
|
+
case types_1.OpCode.LOAD_GLOBAL: {
|
|
485
|
+
const name = names[arg];
|
|
486
|
+
// Find the global (topmost) scope
|
|
487
|
+
let globalScope = frame.scope;
|
|
488
|
+
while (globalScope.parent !== null) {
|
|
489
|
+
globalScope = globalScope.parent;
|
|
490
|
+
}
|
|
491
|
+
const val = globalScope.get(name);
|
|
492
|
+
stack.push(val);
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
case types_1.OpCode.STORE_GLOBAL: {
|
|
496
|
+
const name = names[arg];
|
|
497
|
+
// Find the global (topmost) scope
|
|
498
|
+
let globalScope = frame.scope;
|
|
499
|
+
while (globalScope.parent !== null) {
|
|
500
|
+
globalScope = globalScope.parent;
|
|
501
|
+
}
|
|
502
|
+
globalScope.set(name, stack.pop());
|
|
190
503
|
break;
|
|
191
504
|
}
|
|
192
505
|
case types_1.OpCode.STORE_ATTR: {
|
|
193
|
-
const val =
|
|
194
|
-
const obj =
|
|
506
|
+
const val = stack.pop();
|
|
507
|
+
const obj = stack.pop();
|
|
195
508
|
this.setAttribute(obj, names[arg], val);
|
|
196
509
|
break;
|
|
197
510
|
}
|
|
198
511
|
case types_1.OpCode.LOAD_SUBSCR: {
|
|
199
|
-
const index =
|
|
200
|
-
const obj =
|
|
201
|
-
|
|
512
|
+
const index = stack.pop();
|
|
513
|
+
const obj = stack.pop();
|
|
514
|
+
stack.push(this.getSubscript(obj, index));
|
|
202
515
|
break;
|
|
203
516
|
}
|
|
204
517
|
case types_1.OpCode.STORE_SUBSCR: {
|
|
205
|
-
const index =
|
|
206
|
-
const obj =
|
|
207
|
-
const val =
|
|
518
|
+
const index = stack.pop();
|
|
519
|
+
const obj = stack.pop();
|
|
520
|
+
const val = stack.pop();
|
|
208
521
|
// Check if object is a tuple (immutable)
|
|
209
522
|
if (Array.isArray(obj) && obj.__tuple__) {
|
|
210
523
|
throw new runtime_types_1.PyException('TypeError', `'tuple' object does not support item assignment`);
|
|
@@ -245,8 +558,8 @@ function executeFrame(frame) {
|
|
|
245
558
|
break;
|
|
246
559
|
}
|
|
247
560
|
case types_1.OpCode.DELETE_SUBSCR: {
|
|
248
|
-
const index =
|
|
249
|
-
const obj =
|
|
561
|
+
const index = stack.pop();
|
|
562
|
+
const obj = stack.pop();
|
|
250
563
|
if (Array.isArray(obj)) {
|
|
251
564
|
if (index && (index.__slice__ || index.type === types_1.ASTNodeType.SLICE)) {
|
|
252
565
|
const start = index.start !== undefined ? index.start : null;
|
|
@@ -285,329 +598,155 @@ function executeFrame(frame) {
|
|
|
285
598
|
}
|
|
286
599
|
break;
|
|
287
600
|
}
|
|
288
|
-
|
|
289
|
-
|
|
601
|
+
// UNPACK operations
|
|
602
|
+
case types_1.OpCode.UNPACK_SEQUENCE: {
|
|
603
|
+
const seq = stack.pop();
|
|
604
|
+
const items = Array.isArray(seq) ? seq : Array.from(seq);
|
|
605
|
+
if (items.length !== arg) {
|
|
606
|
+
throw new runtime_types_1.PyException('ValueError', `not enough values to unpack (expected ${arg}, got ${items.length})`);
|
|
607
|
+
}
|
|
608
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
609
|
+
stack.push(items[i]);
|
|
610
|
+
}
|
|
290
611
|
break;
|
|
612
|
+
}
|
|
613
|
+
case types_1.OpCode.UNPACK_EX: {
|
|
614
|
+
const seq = stack.pop();
|
|
615
|
+
const items = Array.isArray(seq) ? seq : Array.from(seq);
|
|
616
|
+
const beforeCount = (arg >> 8) & 0xff;
|
|
617
|
+
const afterCount = arg & 0xff;
|
|
618
|
+
if (items.length < beforeCount + afterCount) {
|
|
619
|
+
throw new runtime_types_1.PyException('ValueError', 'not enough values to unpack');
|
|
620
|
+
}
|
|
621
|
+
const middle = items.slice(beforeCount, items.length - afterCount);
|
|
622
|
+
// Push in reverse order of assignment popping:
|
|
623
|
+
// suffix values (last..first), then middle list, then prefix values (last..first).
|
|
624
|
+
for (let i = afterCount - 1; i >= 0; i--) {
|
|
625
|
+
stack.push(items[items.length - afterCount + i]);
|
|
626
|
+
}
|
|
627
|
+
stack.push(middle);
|
|
628
|
+
for (let i = beforeCount - 1; i >= 0; i--) {
|
|
629
|
+
stack.push(items[i]);
|
|
630
|
+
}
|
|
631
|
+
break;
|
|
632
|
+
}
|
|
633
|
+
// Stack operations
|
|
291
634
|
case types_1.OpCode.DUP_TOP: {
|
|
292
|
-
const val =
|
|
293
|
-
|
|
635
|
+
const val = stack[stack.length - 1];
|
|
636
|
+
stack.push(val);
|
|
294
637
|
break;
|
|
295
638
|
}
|
|
296
639
|
case types_1.OpCode.DUP_TOP_TWO: {
|
|
297
|
-
const top =
|
|
298
|
-
const second =
|
|
299
|
-
|
|
300
|
-
|
|
640
|
+
const top = stack[stack.length - 1];
|
|
641
|
+
const second = stack[stack.length - 2];
|
|
642
|
+
stack.push(second);
|
|
643
|
+
stack.push(top);
|
|
301
644
|
break;
|
|
302
645
|
}
|
|
303
646
|
case types_1.OpCode.ROT_TWO: {
|
|
304
|
-
const a =
|
|
305
|
-
const b =
|
|
306
|
-
|
|
307
|
-
|
|
647
|
+
const a = stack.pop();
|
|
648
|
+
const b = stack.pop();
|
|
649
|
+
stack.push(a);
|
|
650
|
+
stack.push(b);
|
|
308
651
|
break;
|
|
309
652
|
}
|
|
310
653
|
case types_1.OpCode.ROT_THREE: {
|
|
311
|
-
const a =
|
|
312
|
-
const b =
|
|
313
|
-
const c =
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
case types_1.OpCode.BINARY_ADD: {
|
|
320
|
-
const b = frame.stack.pop();
|
|
321
|
-
const a = frame.stack.pop();
|
|
322
|
-
frame.stack.push(this.applyBinary('+', a, b));
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
case types_1.OpCode.BINARY_SUBTRACT: {
|
|
326
|
-
const b = frame.stack.pop();
|
|
327
|
-
const a = frame.stack.pop();
|
|
328
|
-
frame.stack.push(this.applyBinary('-', a, b));
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
case types_1.OpCode.BINARY_MULTIPLY: {
|
|
332
|
-
const b = frame.stack.pop();
|
|
333
|
-
const a = frame.stack.pop();
|
|
334
|
-
frame.stack.push(this.applyBinary('*', a, b));
|
|
335
|
-
break;
|
|
336
|
-
}
|
|
337
|
-
case types_1.OpCode.BINARY_DIVIDE: {
|
|
338
|
-
const b = frame.stack.pop();
|
|
339
|
-
const a = frame.stack.pop();
|
|
340
|
-
frame.stack.push(this.applyBinary('/', a, b));
|
|
341
|
-
break;
|
|
342
|
-
}
|
|
343
|
-
case types_1.OpCode.BINARY_FLOOR_DIVIDE: {
|
|
344
|
-
const b = frame.stack.pop();
|
|
345
|
-
const a = frame.stack.pop();
|
|
346
|
-
frame.stack.push(this.applyBinary('//', a, b));
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
case types_1.OpCode.BINARY_MODULO: {
|
|
350
|
-
const b = frame.stack.pop();
|
|
351
|
-
const a = frame.stack.pop();
|
|
352
|
-
frame.stack.push(this.applyBinary('%', a, b));
|
|
353
|
-
break;
|
|
354
|
-
}
|
|
355
|
-
case types_1.OpCode.BINARY_POWER: {
|
|
356
|
-
const b = frame.stack.pop();
|
|
357
|
-
const a = frame.stack.pop();
|
|
358
|
-
frame.stack.push(this.applyBinary('**', a, b));
|
|
359
|
-
break;
|
|
360
|
-
}
|
|
361
|
-
case types_1.OpCode.BINARY_AND: {
|
|
362
|
-
const b = frame.stack.pop();
|
|
363
|
-
const a = frame.stack.pop();
|
|
364
|
-
frame.stack.push(this.applyBinary('&', a, b));
|
|
365
|
-
break;
|
|
366
|
-
}
|
|
367
|
-
case types_1.OpCode.BINARY_XOR: {
|
|
368
|
-
const b = frame.stack.pop();
|
|
369
|
-
const a = frame.stack.pop();
|
|
370
|
-
frame.stack.push(this.applyBinary('^', a, b));
|
|
371
|
-
break;
|
|
372
|
-
}
|
|
373
|
-
case types_1.OpCode.BINARY_OR: {
|
|
374
|
-
const b = frame.stack.pop();
|
|
375
|
-
const a = frame.stack.pop();
|
|
376
|
-
frame.stack.push(this.applyBinary('|', a, b));
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
case types_1.OpCode.BINARY_LSHIFT: {
|
|
380
|
-
const b = frame.stack.pop();
|
|
381
|
-
const a = frame.stack.pop();
|
|
382
|
-
frame.stack.push(this.applyBinary('<<', a, b));
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
case types_1.OpCode.BINARY_RSHIFT: {
|
|
386
|
-
const b = frame.stack.pop();
|
|
387
|
-
const a = frame.stack.pop();
|
|
388
|
-
frame.stack.push(this.applyBinary('>>', a, b));
|
|
389
|
-
break;
|
|
390
|
-
}
|
|
391
|
-
case types_1.OpCode.INPLACE_ADD: {
|
|
392
|
-
const b = frame.stack.pop();
|
|
393
|
-
const a = frame.stack.pop();
|
|
394
|
-
frame.stack.push(this.applyInPlaceBinary('+', a, b));
|
|
395
|
-
break;
|
|
396
|
-
}
|
|
397
|
-
case types_1.OpCode.INPLACE_SUBTRACT: {
|
|
398
|
-
const b = frame.stack.pop();
|
|
399
|
-
const a = frame.stack.pop();
|
|
400
|
-
frame.stack.push(this.applyInPlaceBinary('-', a, b));
|
|
401
|
-
break;
|
|
402
|
-
}
|
|
403
|
-
case types_1.OpCode.INPLACE_MULTIPLY: {
|
|
404
|
-
const b = frame.stack.pop();
|
|
405
|
-
const a = frame.stack.pop();
|
|
406
|
-
frame.stack.push(this.applyInPlaceBinary('*', a, b));
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
case types_1.OpCode.INPLACE_DIVIDE: {
|
|
410
|
-
const b = frame.stack.pop();
|
|
411
|
-
const a = frame.stack.pop();
|
|
412
|
-
frame.stack.push(this.applyInPlaceBinary('/', a, b));
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
case types_1.OpCode.INPLACE_FLOOR_DIVIDE: {
|
|
416
|
-
const b = frame.stack.pop();
|
|
417
|
-
const a = frame.stack.pop();
|
|
418
|
-
frame.stack.push(this.applyInPlaceBinary('//', a, b));
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
case types_1.OpCode.INPLACE_MODULO: {
|
|
422
|
-
const b = frame.stack.pop();
|
|
423
|
-
const a = frame.stack.pop();
|
|
424
|
-
frame.stack.push(this.applyInPlaceBinary('%', a, b));
|
|
425
|
-
break;
|
|
426
|
-
}
|
|
427
|
-
case types_1.OpCode.INPLACE_POWER: {
|
|
428
|
-
const b = frame.stack.pop();
|
|
429
|
-
const a = frame.stack.pop();
|
|
430
|
-
frame.stack.push(this.applyInPlaceBinary('**', a, b));
|
|
431
|
-
break;
|
|
432
|
-
}
|
|
433
|
-
case types_1.OpCode.INPLACE_AND: {
|
|
434
|
-
const b = frame.stack.pop();
|
|
435
|
-
const a = frame.stack.pop();
|
|
436
|
-
frame.stack.push(this.applyInPlaceBinary('&', a, b));
|
|
437
|
-
break;
|
|
438
|
-
}
|
|
439
|
-
case types_1.OpCode.INPLACE_XOR: {
|
|
440
|
-
const b = frame.stack.pop();
|
|
441
|
-
const a = frame.stack.pop();
|
|
442
|
-
frame.stack.push(this.applyInPlaceBinary('^', a, b));
|
|
443
|
-
break;
|
|
444
|
-
}
|
|
445
|
-
case types_1.OpCode.INPLACE_OR: {
|
|
446
|
-
const b = frame.stack.pop();
|
|
447
|
-
const a = frame.stack.pop();
|
|
448
|
-
frame.stack.push(this.applyInPlaceBinary('|', a, b));
|
|
449
|
-
break;
|
|
450
|
-
}
|
|
451
|
-
case types_1.OpCode.INPLACE_LSHIFT: {
|
|
452
|
-
const b = frame.stack.pop();
|
|
453
|
-
const a = frame.stack.pop();
|
|
454
|
-
frame.stack.push(this.applyInPlaceBinary('<<', a, b));
|
|
455
|
-
break;
|
|
456
|
-
}
|
|
457
|
-
case types_1.OpCode.INPLACE_RSHIFT: {
|
|
458
|
-
const b = frame.stack.pop();
|
|
459
|
-
const a = frame.stack.pop();
|
|
460
|
-
frame.stack.push(this.applyInPlaceBinary('>>', a, b));
|
|
654
|
+
const a = stack.pop();
|
|
655
|
+
const b = stack.pop();
|
|
656
|
+
const c = stack.pop();
|
|
657
|
+
stack.push(a);
|
|
658
|
+
stack.push(c);
|
|
659
|
+
stack.push(b);
|
|
461
660
|
break;
|
|
462
661
|
}
|
|
662
|
+
// UNARY operations
|
|
463
663
|
case types_1.OpCode.UNARY_POSITIVE: {
|
|
464
|
-
const a =
|
|
465
|
-
|
|
664
|
+
const a = stack.pop();
|
|
665
|
+
stack.push(+a);
|
|
466
666
|
break;
|
|
467
667
|
}
|
|
468
668
|
case types_1.OpCode.UNARY_NEGATIVE: {
|
|
469
|
-
const a =
|
|
669
|
+
const a = stack.pop();
|
|
470
670
|
if (typeof a === 'bigint') {
|
|
471
|
-
|
|
671
|
+
stack.push(-a);
|
|
472
672
|
}
|
|
473
673
|
else {
|
|
474
|
-
|
|
674
|
+
stack.push(-a);
|
|
475
675
|
}
|
|
476
676
|
break;
|
|
477
677
|
}
|
|
478
678
|
case types_1.OpCode.UNARY_NOT: {
|
|
479
|
-
const a =
|
|
480
|
-
|
|
679
|
+
const a = stack.pop();
|
|
680
|
+
stack.push(!this.isTruthy(a, frame.scope));
|
|
481
681
|
break;
|
|
482
682
|
}
|
|
483
683
|
case types_1.OpCode.UNARY_INVERT: {
|
|
484
|
-
const a =
|
|
684
|
+
const a = stack.pop();
|
|
485
685
|
if (typeof a === 'bigint') {
|
|
486
|
-
|
|
686
|
+
stack.push(~a);
|
|
487
687
|
}
|
|
488
688
|
else {
|
|
489
|
-
|
|
689
|
+
stack.push(~Number(a));
|
|
490
690
|
}
|
|
491
691
|
break;
|
|
492
692
|
}
|
|
493
|
-
|
|
494
|
-
const b = frame.stack.pop();
|
|
495
|
-
const a = frame.stack.pop();
|
|
496
|
-
frame.stack.push(this.applyCompare(arg, a, b));
|
|
497
|
-
break;
|
|
498
|
-
}
|
|
693
|
+
// BUILD operations
|
|
499
694
|
case types_1.OpCode.BUILD_LIST: {
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
695
|
+
const count = arg;
|
|
696
|
+
const list = new Array(count);
|
|
697
|
+
for (let i = count - 1; i >= 0; i--)
|
|
698
|
+
list[i] = stack.pop();
|
|
699
|
+
stack.push(list);
|
|
504
700
|
break;
|
|
505
701
|
}
|
|
506
702
|
case types_1.OpCode.BUILD_TUPLE: {
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
703
|
+
const count = arg;
|
|
704
|
+
const tuple = new Array(count);
|
|
705
|
+
for (let i = count - 1; i >= 0; i--)
|
|
706
|
+
tuple[i] = stack.pop();
|
|
510
707
|
tuple.__tuple__ = true;
|
|
511
|
-
|
|
708
|
+
stack.push(tuple);
|
|
512
709
|
break;
|
|
513
710
|
}
|
|
514
711
|
case types_1.OpCode.BUILD_MAP: {
|
|
515
712
|
const dict = new runtime_types_1.PyDict();
|
|
516
713
|
const items = [];
|
|
517
714
|
for (let i = 0; i < arg; i++) {
|
|
518
|
-
const v =
|
|
519
|
-
const k =
|
|
715
|
+
const v = stack.pop();
|
|
716
|
+
const k = stack.pop();
|
|
520
717
|
items.push({ k, v });
|
|
521
718
|
}
|
|
522
719
|
for (let i = items.length - 1; i >= 0; i--) {
|
|
523
720
|
dict.set(items[i].k, items[i].v);
|
|
524
721
|
}
|
|
525
|
-
|
|
722
|
+
stack.push(dict);
|
|
526
723
|
break;
|
|
527
724
|
}
|
|
528
725
|
case types_1.OpCode.BUILD_SET: {
|
|
529
726
|
const set = new runtime_types_1.PySet();
|
|
530
727
|
for (let i = 0; i < arg; i++) {
|
|
531
|
-
set.add(
|
|
728
|
+
set.add(stack.pop());
|
|
532
729
|
}
|
|
533
|
-
|
|
730
|
+
stack.push(set);
|
|
534
731
|
break;
|
|
535
732
|
}
|
|
536
733
|
case types_1.OpCode.BUILD_SLICE: {
|
|
537
|
-
const step = arg === 3 ?
|
|
538
|
-
const end =
|
|
539
|
-
const start =
|
|
734
|
+
const step = arg === 3 ? stack.pop() : null;
|
|
735
|
+
const end = stack.pop();
|
|
736
|
+
const start = stack.pop();
|
|
540
737
|
// Create a slice object with start/end/step to match parser AST nodes
|
|
541
|
-
|
|
542
|
-
break;
|
|
543
|
-
}
|
|
544
|
-
case types_1.OpCode.JUMP_ABSOLUTE:
|
|
545
|
-
frame.pc = arg;
|
|
546
|
-
break;
|
|
547
|
-
case types_1.OpCode.POP_JUMP_IF_FALSE: {
|
|
548
|
-
const val = frame.stack.pop();
|
|
549
|
-
if (!this.isTruthy(val, frame.scope)) {
|
|
550
|
-
frame.pc = arg;
|
|
551
|
-
}
|
|
552
|
-
break;
|
|
553
|
-
}
|
|
554
|
-
case types_1.OpCode.POP_JUMP_IF_TRUE: {
|
|
555
|
-
const val = frame.stack.pop();
|
|
556
|
-
if (this.isTruthy(val, frame.scope)) {
|
|
557
|
-
frame.pc = arg;
|
|
558
|
-
}
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
561
|
-
case types_1.OpCode.JUMP_IF_FALSE_OR_POP: {
|
|
562
|
-
const val = frame.stack[frame.stack.length - 1];
|
|
563
|
-
if (!this.isTruthy(val, frame.scope)) {
|
|
564
|
-
frame.pc = arg;
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
frame.stack.pop();
|
|
568
|
-
}
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
|
-
case types_1.OpCode.JUMP_IF_TRUE_OR_POP: {
|
|
572
|
-
const val = frame.stack[frame.stack.length - 1];
|
|
573
|
-
if (this.isTruthy(val, frame.scope)) {
|
|
574
|
-
frame.pc = arg;
|
|
575
|
-
}
|
|
576
|
-
else {
|
|
577
|
-
frame.stack.pop();
|
|
578
|
-
}
|
|
579
|
-
break;
|
|
580
|
-
}
|
|
581
|
-
case types_1.OpCode.GET_ITER: {
|
|
582
|
-
const obj = frame.stack.pop();
|
|
583
|
-
if (obj && typeof obj[Symbol.iterator] === 'function') {
|
|
584
|
-
frame.stack.push(obj[Symbol.iterator]());
|
|
585
|
-
}
|
|
586
|
-
else {
|
|
587
|
-
throw new runtime_types_1.PyException('TypeError', `'${typeof obj}' object is not iterable`);
|
|
588
|
-
}
|
|
589
|
-
break;
|
|
590
|
-
}
|
|
591
|
-
case types_1.OpCode.FOR_ITER: {
|
|
592
|
-
const iter = frame.stack[frame.stack.length - 1];
|
|
593
|
-
const next = iter.next();
|
|
594
|
-
if (next.done) {
|
|
595
|
-
frame.stack.pop();
|
|
596
|
-
frame.pc = arg;
|
|
597
|
-
}
|
|
598
|
-
else {
|
|
599
|
-
frame.stack.push(next.value);
|
|
600
|
-
}
|
|
738
|
+
stack.push({ __slice__: true, start, end, step });
|
|
601
739
|
break;
|
|
602
740
|
}
|
|
741
|
+
// Function/class operations
|
|
603
742
|
case types_1.OpCode.MAKE_FUNCTION: {
|
|
604
743
|
const defaultsCount = arg || 0;
|
|
605
|
-
const name =
|
|
606
|
-
const bc =
|
|
607
|
-
const defaults =
|
|
744
|
+
const name = stack.pop();
|
|
745
|
+
const bc = stack.pop();
|
|
746
|
+
const defaults = new Array(defaultsCount);
|
|
608
747
|
// Pop default values from stack in reverse order (last default on top)
|
|
609
|
-
for (let i =
|
|
610
|
-
defaults
|
|
748
|
+
for (let i = defaultsCount - 1; i >= 0; i--) {
|
|
749
|
+
defaults[i] = stack.pop();
|
|
611
750
|
}
|
|
612
751
|
// Create a copy of params with evaluated defaults
|
|
613
752
|
const params = (bc && bc.params) ? bc.params.map((p) => ({ ...p })) : [];
|
|
@@ -623,12 +762,54 @@ function executeFrame(frame) {
|
|
|
623
762
|
const body = (bc && bc.astBody) ? bc.astBody : [];
|
|
624
763
|
const func = new runtime_types_1.PyFunction(name, params, body, frame.scope, isGenerator, new Set(), bc);
|
|
625
764
|
func.closure_shared_values = frame.scope.values;
|
|
626
|
-
|
|
765
|
+
stack.push(func);
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
case types_1.OpCode.CALL_FUNCTION_KW: {
|
|
769
|
+
const kwNames = stack.pop();
|
|
770
|
+
const kwList = Array.isArray(kwNames) ? kwNames : [];
|
|
771
|
+
const kwCount = kwList.length;
|
|
772
|
+
const total = arg;
|
|
773
|
+
const values = new Array(total);
|
|
774
|
+
for (let i = total - 1; i >= 0; i--) {
|
|
775
|
+
values[i] = stack.pop();
|
|
776
|
+
}
|
|
777
|
+
const func = stack.pop();
|
|
778
|
+
const positionalCount = total - kwCount;
|
|
779
|
+
const positional = values.slice(0, positionalCount);
|
|
780
|
+
const kwargs = {};
|
|
781
|
+
for (let i = 0; i < kwCount; i++) {
|
|
782
|
+
kwargs[String(kwList[i])] = values[positionalCount + i];
|
|
783
|
+
}
|
|
784
|
+
stack.push(this.callFunction(func, positional, frame.scope, kwargs));
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
case types_1.OpCode.CALL_FUNCTION_EX: {
|
|
788
|
+
// arg == 1 means there's a kwargs dict on top
|
|
789
|
+
// Stack: [func, args_tuple] or [func, args_tuple, kwargs_dict]
|
|
790
|
+
const kwargs = {};
|
|
791
|
+
if (arg === 1) {
|
|
792
|
+
const kwDict = stack.pop();
|
|
793
|
+
if (kwDict instanceof runtime_types_1.PyDict) {
|
|
794
|
+
for (const [k, v] of kwDict.entries()) {
|
|
795
|
+
kwargs[String(k)] = v;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
else if (kwDict && typeof kwDict === 'object') {
|
|
799
|
+
for (const [k, v] of Object.entries(kwDict)) {
|
|
800
|
+
kwargs[k] = v;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
const argsTuple = stack.pop();
|
|
805
|
+
const func = stack.pop();
|
|
806
|
+
const args = Array.isArray(argsTuple) ? argsTuple : (argsTuple ? Array.from(argsTuple) : []);
|
|
807
|
+
stack.push(this.callFunction(func, args, frame.scope, kwargs));
|
|
627
808
|
break;
|
|
628
809
|
}
|
|
629
810
|
case types_1.OpCode.LOAD_BUILD_CLASS: {
|
|
630
811
|
const enclosingScope = frame.scope;
|
|
631
|
-
|
|
812
|
+
stack.push((bodyFn, name, ...bases) => {
|
|
632
813
|
const classScope = new runtime_types_1.Scope(enclosingScope, true);
|
|
633
814
|
if (bodyFn instanceof runtime_types_1.PyFunction && bodyFn.bytecode) {
|
|
634
815
|
const bodyFrame = new runtime_types_1.Frame(bodyFn.bytecode, classScope);
|
|
@@ -644,46 +825,47 @@ function executeFrame(frame) {
|
|
|
644
825
|
});
|
|
645
826
|
break;
|
|
646
827
|
}
|
|
828
|
+
// Import/exception operations
|
|
647
829
|
case types_1.OpCode.IMPORT_NAME: {
|
|
648
|
-
|
|
649
|
-
|
|
830
|
+
stack.pop(); // level
|
|
831
|
+
stack.pop(); // fromlist
|
|
650
832
|
const name = names[arg];
|
|
651
|
-
|
|
833
|
+
stack.push(this.importModule(name, frame.scope));
|
|
652
834
|
break;
|
|
653
835
|
}
|
|
654
836
|
case types_1.OpCode.RAISE_VARARGS: {
|
|
655
837
|
if (arg >= 2)
|
|
656
|
-
|
|
657
|
-
const exc = arg >= 1 ?
|
|
838
|
+
stack.pop(); // cause
|
|
839
|
+
const exc = arg >= 1 ? stack.pop() : null;
|
|
658
840
|
// simplified raise
|
|
659
841
|
throw exc || new runtime_types_1.PyException('RuntimeError', 'No exception to raise');
|
|
660
842
|
}
|
|
661
843
|
case types_1.OpCode.SETUP_FINALLY: {
|
|
662
|
-
frame.blockStack.push({ handler: arg, stackHeight:
|
|
844
|
+
frame.blockStack.push({ handler: arg, stackHeight: stack.length });
|
|
663
845
|
break;
|
|
664
846
|
}
|
|
665
847
|
case types_1.OpCode.SETUP_WITH: {
|
|
666
|
-
const ctx =
|
|
848
|
+
const ctx = stack.pop();
|
|
667
849
|
const enter = this.getAttribute(ctx, '__enter__', frame.scope);
|
|
668
850
|
const exit = this.getAttribute(ctx, '__exit__', frame.scope);
|
|
669
851
|
const result = this.callFunction(enter, [], frame.scope);
|
|
670
|
-
|
|
671
|
-
|
|
852
|
+
stack.push(exit);
|
|
853
|
+
stack.push(result);
|
|
672
854
|
// Block should assume exit is on stack, so stackHeight includes exit.
|
|
673
855
|
// When popping block, we leave exit on stack?
|
|
674
856
|
// No, standard behavior: SETUP_WITH pushes exit, enter_res.
|
|
675
857
|
// Handler expects [exit, exc...]?
|
|
676
858
|
// Let's rely on blockStack logic: stack returned to stackHeight on exception.
|
|
677
859
|
// If we set stackHeight to include exit, then on exception, we have [exit], then push exc.
|
|
678
|
-
frame.blockStack.push({ handler: arg, stackHeight:
|
|
860
|
+
frame.blockStack.push({ handler: arg, stackHeight: stack.length - 1 });
|
|
679
861
|
break;
|
|
680
862
|
}
|
|
681
863
|
case types_1.OpCode.WITH_EXCEPT_START: {
|
|
682
864
|
// Stack: [exit, exc_norm] (after normalizeThrown in dispatchException)
|
|
683
865
|
// Or [exit, exc]
|
|
684
866
|
// This opcode calls exit(type, val, tb)
|
|
685
|
-
const exc =
|
|
686
|
-
const exit =
|
|
867
|
+
const exc = stack.pop();
|
|
868
|
+
const exit = stack.pop();
|
|
687
869
|
// Call exit(type, val, tb)
|
|
688
870
|
// For our VM, we can pass (exc.type, exc, None)
|
|
689
871
|
const pyType = (exc instanceof runtime_types_1.PyInstance) ? exc.klass : (exc.pyType || exc);
|
|
@@ -707,64 +889,11 @@ function executeFrame(frame) {
|
|
|
707
889
|
frame.blockStack.pop();
|
|
708
890
|
break;
|
|
709
891
|
}
|
|
710
|
-
case types_1.OpCode.CALL_FUNCTION: {
|
|
711
|
-
const args = [];
|
|
712
|
-
for (let i = 0; i < arg; i++) {
|
|
713
|
-
args.unshift(frame.stack.pop());
|
|
714
|
-
}
|
|
715
|
-
const func = frame.stack.pop();
|
|
716
|
-
frame.stack.push(this.callFunction(func, args, frame.scope));
|
|
717
|
-
break;
|
|
718
|
-
}
|
|
719
|
-
case types_1.OpCode.CALL_FUNCTION_KW: {
|
|
720
|
-
const kwNames = frame.stack.pop();
|
|
721
|
-
const kwList = Array.isArray(kwNames) ? kwNames : [];
|
|
722
|
-
const kwCount = kwList.length;
|
|
723
|
-
const total = arg;
|
|
724
|
-
const values = [];
|
|
725
|
-
for (let i = 0; i < total; i++) {
|
|
726
|
-
values.unshift(frame.stack.pop());
|
|
727
|
-
}
|
|
728
|
-
const func = frame.stack.pop();
|
|
729
|
-
const positionalCount = total - kwCount;
|
|
730
|
-
const positional = values.slice(0, positionalCount);
|
|
731
|
-
const kwargs = {};
|
|
732
|
-
for (let i = 0; i < kwCount; i++) {
|
|
733
|
-
kwargs[String(kwList[i])] = values[positionalCount + i];
|
|
734
|
-
}
|
|
735
|
-
frame.stack.push(this.callFunction(func, positional, frame.scope, kwargs));
|
|
736
|
-
break;
|
|
737
|
-
}
|
|
738
|
-
case types_1.OpCode.CALL_FUNCTION_EX: {
|
|
739
|
-
// arg == 1 means there's a kwargs dict on top
|
|
740
|
-
// Stack: [func, args_tuple] or [func, args_tuple, kwargs_dict]
|
|
741
|
-
const kwargs = {};
|
|
742
|
-
if (arg === 1) {
|
|
743
|
-
const kwDict = frame.stack.pop();
|
|
744
|
-
if (kwDict instanceof runtime_types_1.PyDict) {
|
|
745
|
-
for (const [k, v] of kwDict.entries()) {
|
|
746
|
-
kwargs[String(k)] = v;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
else if (kwDict && typeof kwDict === 'object') {
|
|
750
|
-
for (const [k, v] of Object.entries(kwDict)) {
|
|
751
|
-
kwargs[k] = v;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
const argsTuple = frame.stack.pop();
|
|
756
|
-
const func = frame.stack.pop();
|
|
757
|
-
const args = Array.isArray(argsTuple) ? argsTuple : (argsTuple ? Array.from(argsTuple) : []);
|
|
758
|
-
frame.stack.push(this.callFunction(func, args, frame.scope, kwargs));
|
|
759
|
-
break;
|
|
760
|
-
}
|
|
761
892
|
case types_1.OpCode.EVAL_AST: {
|
|
762
|
-
const node =
|
|
763
|
-
|
|
893
|
+
const node = stack.pop();
|
|
894
|
+
stack.push(this.evaluateExpression(node, frame.scope));
|
|
764
895
|
break;
|
|
765
896
|
}
|
|
766
|
-
case types_1.OpCode.RETURN_VALUE:
|
|
767
|
-
return frame.stack.pop();
|
|
768
897
|
default:
|
|
769
898
|
throw new Error(`VM: Unknown opcode ${types_1.OpCode[opcode]} (${opcode}) at pc ${frame.pc - 1}`);
|
|
770
899
|
}
|