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