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