@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.
@@ -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 (frame.locals[i] !== undefined) {
52
+ else if (locals[i] !== undefined) {
47
53
  if (process.env['DEBUG_NONLOCAL']) {
48
- console.log(`renderFString: varname=${varname}, frame.locals[${i}]=${frame.locals[i]}`);
54
+ console.log(`renderFString: varname=${varname}, locals[${i}]=${locals[i]}`);
49
55
  }
50
- evalScope.values.set(varname, frame.locals[i]);
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
- frame.stack.length = block.stackHeight;
80
- frame.stack.push(normalizeThrown(err));
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
- const varname = varnames[arg];
100
- if (varname !== undefined && frame.scope.values.has(varname)) {
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
- throw new runtime_types_1.PyException('UnboundLocalError', `local variable '${varname}' referenced before assignment`);
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
- frame.stack.push(val);
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
- frame.stack.push(renderFString(val.__fstring__, frame.scope));
125
+ stack.push(renderFString(val.__fstring__, frame.scope));
118
126
  }
119
127
  else {
120
- frame.stack.push(val);
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 = frame.scope.get(name);
134
+ const val = scope.get(name);
127
135
  // console.log(`LOAD_NAME ${name} -> ${val}`);
128
- frame.stack.push(val);
136
+ stack.push(val);
129
137
  break;
130
138
  }
131
139
  case types_1.OpCode.BINARY_ADD: {
132
- const b = frame.stack.pop();
133
- const a = frame.stack.pop();
134
- frame.stack.push(this.applyBinary('+', a, b));
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 = frame.stack.pop();
139
- const a = frame.stack.pop();
140
- frame.stack.push(this.applyBinary('-', a, b));
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 = frame.stack.pop();
145
- const a = frame.stack.pop();
146
- frame.stack.push(this.applyBinary('*', a, b));
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 args = [];
151
- for (let i = 0; i < arg; i++) {
152
- args.unshift(frame.stack.pop());
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 = frame.stack.pop();
155
- frame.stack.push(this.callFunction(func, args, frame.scope));
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 frame.stack.pop();
187
+ return stack.pop();
160
188
  case types_1.OpCode.COMPARE_OP: {
161
- const b = frame.stack.pop();
162
- const a = frame.stack.pop();
163
- frame.stack.push(this.applyCompare(arg, a, b));
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 = frame.stack.pop();
168
- if (!this.isTruthy(val, frame.scope)) {
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 = frame.stack.pop();
175
- frame.locals[arg] = val;
248
+ const val = stack.pop();
249
+ locals[arg] = val;
176
250
  if (varnames && varnames[arg] !== undefined) {
177
- frame.scope.values.set(varnames[arg], val);
251
+ scopeValues.set(varnames[arg], val);
178
252
  }
179
253
  break;
180
254
  }
181
255
  case types_1.OpCode.STORE_NAME:
182
- frame.scope.set(names[arg], frame.stack.pop());
256
+ scope.set(names[arg], stack.pop());
183
257
  break;
184
258
  case types_1.OpCode.FOR_ITER: {
185
- const iter = frame.stack[frame.stack.length - 1];
259
+ const iter = stack[stack.length - 1];
186
260
  const next = iter.next();
187
261
  if (next.done) {
188
- frame.stack.pop();
262
+ stack.pop();
189
263
  frame.pc = arg;
190
264
  }
191
265
  else {
192
- frame.stack.push(next.value);
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 = frame.stack.pop();
201
- const a = frame.stack.pop();
202
- frame.stack.push(this.applyInPlaceBinary('+', a, b));
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 = frame.stack.pop();
207
- const a = frame.stack.pop();
208
- frame.stack.push(this.applyInPlaceBinary('-', a, b));
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 = frame.stack.pop();
213
- const a = frame.stack.pop();
214
- frame.stack.push(this.applyInPlaceBinary('*', a, b));
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 = frame.stack.pop();
310
+ lastValue = stack.pop();
219
311
  break;
220
312
  case types_1.OpCode.GET_ITER: {
221
- const obj = frame.stack.pop();
222
- if (obj && typeof obj[Symbol.iterator] === 'function') {
223
- frame.stack.push(obj[Symbol.iterator]());
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 = frame.stack.pop();
232
- frame.stack.push(this.getAttribute(obj, names[arg], frame.scope));
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 = frame.stack.pop();
238
- const a = frame.stack.pop();
239
- frame.stack.push(this.applyBinary('/', a, b));
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 = frame.stack.pop();
244
- const a = frame.stack.pop();
245
- frame.stack.push(this.applyBinary('//', a, b));
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 = frame.stack.pop();
250
- const a = frame.stack.pop();
251
- frame.stack.push(this.applyBinary('%', a, b));
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 = frame.stack.pop();
256
- const a = frame.stack.pop();
257
- frame.stack.push(this.applyBinary('**', a, b));
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 = frame.stack.pop();
262
- const a = frame.stack.pop();
263
- frame.stack.push(this.applyBinary('&', a, b));
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 = frame.stack.pop();
268
- const a = frame.stack.pop();
269
- frame.stack.push(this.applyBinary('^', a, b));
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 = frame.stack.pop();
274
- const a = frame.stack.pop();
275
- frame.stack.push(this.applyBinary('|', a, b));
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 = frame.stack.pop();
280
- const a = frame.stack.pop();
281
- frame.stack.push(this.applyBinary('<<', a, b));
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 = frame.stack.pop();
286
- const a = frame.stack.pop();
287
- frame.stack.push(this.applyBinary('>>', a, b));
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 = frame.stack.pop();
293
- const a = frame.stack.pop();
294
- frame.stack.push(this.applyInPlaceBinary('/', a, b));
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 = frame.stack.pop();
299
- const a = frame.stack.pop();
300
- frame.stack.push(this.applyInPlaceBinary('//', a, b));
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 = frame.stack.pop();
305
- const a = frame.stack.pop();
306
- frame.stack.push(this.applyInPlaceBinary('%', a, b));
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 = frame.stack.pop();
311
- const a = frame.stack.pop();
312
- frame.stack.push(this.applyInPlaceBinary('**', a, b));
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 = frame.stack.pop();
317
- const a = frame.stack.pop();
318
- frame.stack.push(this.applyInPlaceBinary('&', a, b));
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 = frame.stack.pop();
323
- const a = frame.stack.pop();
324
- frame.stack.push(this.applyInPlaceBinary('^', a, b));
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 = frame.stack.pop();
329
- const a = frame.stack.pop();
330
- frame.stack.push(this.applyInPlaceBinary('|', a, b));
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 = frame.stack.pop();
335
- const a = frame.stack.pop();
336
- frame.stack.push(this.applyInPlaceBinary('<<', a, b));
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 = frame.stack.pop();
341
- const a = frame.stack.pop();
342
- frame.stack.push(this.applyInPlaceBinary('>>', a, b));
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 = frame.stack.pop();
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 = frame.stack[frame.stack.length - 1];
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
- frame.stack.pop();
469
+ stack.pop();
360
470
  }
361
471
  break;
362
472
  }
363
473
  case types_1.OpCode.JUMP_IF_TRUE_OR_POP: {
364
- const val = frame.stack[frame.stack.length - 1];
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
- frame.stack.pop();
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
- frame.stack.push(val);
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, frame.stack.pop());
502
+ globalScope.set(name, stack.pop());
393
503
  break;
394
504
  }
395
505
  case types_1.OpCode.STORE_ATTR: {
396
- const val = frame.stack.pop();
397
- const obj = frame.stack.pop();
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 = frame.stack.pop();
403
- const obj = frame.stack.pop();
404
- frame.stack.push(this.getSubscript(obj, index));
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 = frame.stack.pop();
409
- const obj = frame.stack.pop();
410
- const val = frame.stack.pop();
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 = frame.stack.pop();
452
- const obj = frame.stack.pop();
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 = frame.stack.pop();
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
- frame.stack.push(items[i]);
609
+ stack.push(items[i]);
500
610
  }
501
611
  break;
502
612
  }
503
613
  case types_1.OpCode.UNPACK_EX: {
504
- const seq = frame.stack.pop();
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
- frame.stack.push(items[items.length - afterCount + i]);
625
+ stack.push(items[items.length - afterCount + i]);
516
626
  }
517
- frame.stack.push(middle);
627
+ stack.push(middle);
518
628
  for (let i = beforeCount - 1; i >= 0; i--) {
519
- frame.stack.push(items[i]);
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 = frame.stack[frame.stack.length - 1];
526
- frame.stack.push(val);
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 = frame.stack[frame.stack.length - 1];
531
- const second = frame.stack[frame.stack.length - 2];
532
- frame.stack.push(second);
533
- frame.stack.push(top);
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 = frame.stack.pop();
538
- const b = frame.stack.pop();
539
- frame.stack.push(a);
540
- frame.stack.push(b);
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 = frame.stack.pop();
545
- const b = frame.stack.pop();
546
- const c = frame.stack.pop();
547
- frame.stack.push(a);
548
- frame.stack.push(c);
549
- frame.stack.push(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);
550
660
  break;
551
661
  }
552
662
  // UNARY operations
553
663
  case types_1.OpCode.UNARY_POSITIVE: {
554
- const a = frame.stack.pop();
555
- frame.stack.push(+a);
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 = frame.stack.pop();
669
+ const a = stack.pop();
560
670
  if (typeof a === 'bigint') {
561
- frame.stack.push(-a);
671
+ stack.push(-a);
562
672
  }
563
673
  else {
564
- frame.stack.push(-a);
674
+ stack.push(-a);
565
675
  }
566
676
  break;
567
677
  }
568
678
  case types_1.OpCode.UNARY_NOT: {
569
- const a = frame.stack.pop();
570
- frame.stack.push(!this.isTruthy(a, frame.scope));
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 = frame.stack.pop();
684
+ const a = stack.pop();
575
685
  if (typeof a === 'bigint') {
576
- frame.stack.push(~a);
686
+ stack.push(~a);
577
687
  }
578
688
  else {
579
- frame.stack.push(~Number(a));
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 list = [];
586
- for (let i = 0; i < arg; i++)
587
- list.unshift(frame.stack.pop());
588
- frame.stack.push(list);
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 tuple = [];
593
- for (let i = 0; i < arg; i++)
594
- tuple.unshift(frame.stack.pop());
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
- frame.stack.push(tuple);
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 = frame.stack.pop();
604
- const k = frame.stack.pop();
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
- frame.stack.push(dict);
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(frame.stack.pop());
728
+ set.add(stack.pop());
617
729
  }
618
- frame.stack.push(set);
730
+ stack.push(set);
619
731
  break;
620
732
  }
621
733
  case types_1.OpCode.BUILD_SLICE: {
622
- const step = arg === 3 ? frame.stack.pop() : null;
623
- const end = frame.stack.pop();
624
- const start = frame.stack.pop();
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
- frame.stack.push({ __slice__: true, start, end, step });
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 = frame.stack.pop();
633
- const bc = frame.stack.pop();
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 = 0; i < defaultsCount; i++) {
637
- defaults.unshift(frame.stack.pop());
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
- frame.stack.push(func);
765
+ stack.push(func);
654
766
  break;
655
767
  }
656
768
  case types_1.OpCode.CALL_FUNCTION_KW: {
657
- const kwNames = frame.stack.pop();
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 = 0; i < total; i++) {
663
- values.unshift(frame.stack.pop());
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 = frame.stack.pop();
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
- frame.stack.push(this.callFunction(func, positional, frame.scope, kwargs));
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 = frame.stack.pop();
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 = frame.stack.pop();
693
- const func = frame.stack.pop();
804
+ const argsTuple = stack.pop();
805
+ const func = stack.pop();
694
806
  const args = Array.isArray(argsTuple) ? argsTuple : (argsTuple ? Array.from(argsTuple) : []);
695
- frame.stack.push(this.callFunction(func, args, frame.scope, kwargs));
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
- frame.stack.push((bodyFn, name, ...bases) => {
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
- frame.stack.pop(); // level
719
- frame.stack.pop(); // fromlist
830
+ stack.pop(); // level
831
+ stack.pop(); // fromlist
720
832
  const name = names[arg];
721
- frame.stack.push(this.importModule(name, frame.scope));
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
- frame.stack.pop(); // cause
727
- const exc = arg >= 1 ? frame.stack.pop() : null;
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: frame.stack.length });
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 = frame.stack.pop();
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
- frame.stack.push(exit);
741
- frame.stack.push(result);
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: frame.stack.length - 1 });
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 = frame.stack.pop();
756
- const exit = frame.stack.pop();
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 = frame.stack.pop();
782
- frame.stack.push(this.evaluateExpression(node, frame.scope));
893
+ const node = stack.pop();
894
+ stack.push(this.evaluateExpression(node, frame.scope));
783
895
  break;
784
896
  }
785
897
  default: