diff options
Diffstat (limited to 'ldebug.c')
-rw-r--r-- | ldebug.c | 360 |
1 files changed, 195 insertions, 165 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: ldebug.c,v 1.75 2001/03/26 14:31:49 roberto Exp roberto $ | 2 | ** $Id: ldebug.c,v 1.76 2001/04/06 18:25:00 roberto Exp roberto $ |
3 | ** Debug Interface | 3 | ** Debug Interface |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -22,6 +22,7 @@ | |||
22 | #include "ltable.h" | 22 | #include "ltable.h" |
23 | #include "ltm.h" | 23 | #include "ltm.h" |
24 | #include "luadebug.h" | 24 | #include "luadebug.h" |
25 | #include "lvm.h" | ||
25 | 26 | ||
26 | 27 | ||
27 | 28 | ||
@@ -298,6 +299,10 @@ LUA_API int lua_getinfo (lua_State *L, const l_char *what, lua_Debug *ar) { | |||
298 | 299 | ||
299 | #define check(x) if (!(x)) return 0; | 300 | #define check(x) if (!(x)) return 0; |
300 | 301 | ||
302 | #define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode) | ||
303 | |||
304 | #define checkreg(pt,reg) check((reg) < (pt)->maxstacksize) | ||
305 | |||
301 | 306 | ||
302 | static int checklineinfo (const Proto *pt) { | 307 | static int checklineinfo (const Proto *pt) { |
303 | int *lineinfo = pt->lineinfo; | 308 | int *lineinfo = pt->lineinfo; |
@@ -318,231 +323,199 @@ static int precheck (const Proto *pt) { | |||
318 | } | 323 | } |
319 | 324 | ||
320 | 325 | ||
321 | /* value for non-initialized entries in array stacklevel */ | 326 | static int checkopenop (Instruction i) { |
322 | #define SL_EMPTY 255 | 327 | OpCode op = GET_OPCODE(i); |
323 | 328 | switch (op) { | |
324 | #define checkjump(pt,sl,top,pc) if (!checkjump_aux(pt,sl,top,pc)) return 0; | 329 | case OP_CALL: |
325 | 330 | case OP_RETURN: { | |
326 | static int checkjump_aux (const Proto *pt, lu_byte *sl, int top, int pc) { | 331 | check(GETARG_B(i) == NO_REG); |
327 | check(0 <= pc && pc < pt->sizecode); | 332 | return 1; |
328 | if (sl == NULL) return 1; /* not full checking */ | 333 | } |
329 | if (sl[pc] == SL_EMPTY) | 334 | case OP_SETLISTO: return 1; |
330 | sl[pc] = (lu_byte)top; | 335 | default: return 0; /* invalid instruction after an open call */ |
331 | else | 336 | } |
332 | check(sl[pc] == top); | ||
333 | return 1; | ||
334 | } | 337 | } |
335 | 338 | ||
336 | 339 | ||
337 | static Instruction luaG_symbexec (lua_State *L, const Proto *pt, | 340 | static Instruction luaG_symbexec (const Proto *pt, int lastpc, int reg) { |
338 | int lastpc, int stackpos) { | ||
339 | int stack[MAXSTACK]; /* stores last instruction that changed a stack entry */ | ||
340 | lu_byte *sl = NULL; | ||
341 | int top; | ||
342 | int pc; | 341 | int pc; |
343 | if (stackpos < 0) { /* full check? */ | 342 | int last; /* stores position of last instruction that changed `reg' */ |
344 | int i; | 343 | last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */ |
345 | sl = luaO_openspace(L, pt->sizecode, lu_byte); | 344 | if (reg == NO_REG) /* full check? */ |
346 | for (i=0; i<pt->sizecode; i++) /* initialize stack-level array */ | ||
347 | sl[i] = SL_EMPTY; | ||
348 | check(precheck(pt)); | 345 | check(precheck(pt)); |
349 | } | 346 | for (pc = 0; pc < lastpc; pc++) { |
350 | top = pt->numparams; | 347 | const Instruction i = pt->code[pc]; |
351 | pc = 0; | ||
352 | if (pt->is_vararg) /* varargs? */ | ||
353 | top++; /* `arg' */ | ||
354 | if (sl) sl[0] = (lu_byte)top; | ||
355 | while (pc < lastpc) { | ||
356 | const Instruction i = pt->code[pc++]; | ||
357 | OpCode op = GET_OPCODE(i); | 348 | OpCode op = GET_OPCODE(i); |
358 | int arg1 = 0; | 349 | int a = GETARG_A(i); |
359 | int arg2 = 0; | 350 | int b = 0; |
360 | int push, pop; | 351 | int c = 0; |
361 | check(op < NUM_OPCODES); | 352 | #undef check |
362 | push = (int)luaK_opproperties[op].push; | 353 | #define check(x) if (!(x)) { \ |
363 | pop = (int)luaK_opproperties[op].pop; | 354 | printf(">>>%d %d %d %d %d %d\n", op, a, b, c, pt->maxstacksize, pt->sizek); \ |
364 | switch ((enum Mode)luaK_opproperties[op].mode) { | 355 | return 0; } |
365 | case iO: break; | 356 | switch (getOpMode(op)) { |
366 | case iU: arg1 = GETARG_U(i); check(arg1 >= 0); break; | 357 | case iABC: { |
367 | case iS: arg1 = GETARG_S(i); break; | 358 | b = GETARG_B(i); |
368 | case iAB: | 359 | c = GETARG_C(i); |
369 | arg1 = GETARG_A(i); arg2 = GETARG_B(i); check(arg1 >= 0); break; | 360 | if (testOpMode(op, OpModeBreg)) { |
370 | } | 361 | checkreg(pt, b); |
371 | switch (op) { | 362 | check(c < pt->maxstacksize || |
372 | case OP_RETURN: { | 363 | (c >= MAXSTACK && c-MAXSTACK < pt->sizek)); |
373 | check(arg1 <= top); | 364 | } |
374 | pop = top-arg1; | ||
375 | break; | 365 | break; |
376 | } | 366 | } |
377 | case OP_CALL: { | 367 | case iABc: { |
378 | if (arg2 == MULT_RET) arg2 = 1; | 368 | b = GETARG_Bc(i); |
379 | check(arg1 < top); | 369 | if (testOpMode(op, OpModeK)) check(b < pt->sizek); |
380 | pop = top-arg1; | ||
381 | push = arg2; | ||
382 | break; | 370 | break; |
383 | } | 371 | } |
384 | case OP_PUSHNIL: { | 372 | case iAsBc: { |
385 | check(arg1 > 0); | 373 | b = GETARG_sBc(i); |
386 | push = arg1; | ||
387 | break; | 374 | break; |
388 | } | 375 | } |
389 | case OP_POP: { | 376 | } |
390 | pop = arg1; | 377 | if (testOpMode(op, OpModeAreg)) checkreg(pt, a); |
378 | if (testOpMode(op, OpModesetA)) { | ||
379 | if (a == reg) last = pc; /* change register `a' */ | ||
380 | } | ||
381 | if (testOpMode(op, OpModeT)) | ||
382 | check(GET_OPCODE(pt->code[pc+1]) == OP_CJMP); | ||
383 | switch (op) { | ||
384 | case OP_LOADNIL: { | ||
385 | if (a <= reg && reg <= b) | ||
386 | last = pc; /* set registers from `a' to `b' */ | ||
391 | break; | 387 | break; |
392 | } | 388 | } |
393 | case OP_PUSHSTRING: | 389 | case OP_LOADUPVAL: { |
394 | case OP_GETGLOBAL: | 390 | check(b < pt->nupvalues); |
395 | case OP_GETDOTTED: | ||
396 | case OP_PUSHSELF: | ||
397 | case OP_SETGLOBAL: { | ||
398 | check(arg1 < pt->sizekstr); | ||
399 | break; | 391 | break; |
400 | } | 392 | } |
401 | case OP_PUSHNUM: | 393 | case OP_GETGLOBAL: |
402 | case OP_PUSHNEGNUM: { | 394 | case OP_SETGLOBAL: { |
403 | check(arg1 < pt->sizeknum); | 395 | check(ttype(&pt->k[b]) == LUA_TSTRING); |
404 | break; | 396 | break; |
405 | } | 397 | } |
406 | case OP_PUSHUPVALUE: { | 398 | case OP_SELF: { |
407 | check(arg1 < pt->nupvalues); | 399 | checkreg(pt, a+1); |
400 | if (reg == a+1) last = pc; | ||
408 | break; | 401 | break; |
409 | } | 402 | } |
410 | case OP_GETLOCAL: | 403 | case OP_CONCAT: { |
411 | case OP_GETINDEXED: | 404 | check(b < c); /* at least two operands */ |
412 | case OP_SETLOCAL: { | ||
413 | check(arg1 < top); | ||
414 | break; | 405 | break; |
415 | } | 406 | } |
416 | case OP_SETTABLE: { | 407 | case OP_JMP: |
417 | check(3 <= arg1 && arg1 <= top); | 408 | case OP_CJMP: { |
418 | pop = arg2; | 409 | int dest = pc+1+b; |
410 | check(0 <= dest && dest < pt->sizecode); | ||
411 | /* not full check and jump is forward and do not skip `lastpc'? */ | ||
412 | if (reg != NO_REG && pc < dest && dest <= lastpc) | ||
413 | pc += b; /* do the jump */ | ||
419 | break; | 414 | break; |
420 | } | 415 | } |
421 | case OP_SETLIST: { | 416 | case OP_TESTT: |
422 | check(arg2 >= 0); | 417 | case OP_TESTF: { |
423 | pop = top-arg2-1; | 418 | if (a != NO_REG) |
419 | checkreg(pt, a); | ||
424 | break; | 420 | break; |
425 | } | 421 | } |
426 | case OP_SETMAP: { | 422 | case OP_NILJMP: { |
427 | check(arg1 >= 0); | 423 | check(pc+2 < pt->sizecode); /* check its jump */ |
428 | pop = top-arg1-1; | ||
429 | break; | 424 | break; |
430 | } | 425 | } |
431 | case OP_CONCAT: { | 426 | case OP_CALL: { |
432 | pop = arg1; | 427 | if (b == NO_REG) b = pt->maxstacksize; |
428 | if (c == NO_REG) { | ||
429 | check(checkopenop(pt->code[pc+1])); | ||
430 | c = 1; | ||
431 | } | ||
432 | check(b > a); | ||
433 | checkreg(pt, b-1); | ||
434 | checkreg(pt, a+c-1); | ||
435 | if (reg >= a) last = pc; /* affect all registers above base */ | ||
433 | break; | 436 | break; |
434 | } | 437 | } |
435 | case OP_CLOSURE: { | 438 | case OP_RETURN: { |
436 | check(arg1 < pt->sizekproto); | 439 | if (b == NO_REG) b = pt->maxstacksize; |
437 | check(arg2 == pt->kproto[arg1]->nupvalues); | 440 | checkreg(pt, b-1); |
438 | pop = arg2; | ||
439 | break; | 441 | break; |
440 | } | 442 | } |
441 | case OP_JMPNE: | 443 | case OP_FORPREP: |
442 | case OP_JMPEQ: | 444 | case OP_TFORPREP: { |
443 | case OP_JMPLT: | 445 | int dest = pc-b; /* jump is negated here */ |
444 | case OP_JMPLE: | 446 | check(0 <= dest && dest < pt->sizecode && |
445 | case OP_JMPGT: | 447 | GET_OPCODE(pt->code[dest]) == op+1); |
446 | case OP_JMPGE: | ||
447 | case OP_JMPT: | ||
448 | case OP_JMPF: | ||
449 | case OP_JMP: { | ||
450 | checkjump(pt, sl, top-pop, pc+arg1); | ||
451 | break; | 448 | break; |
452 | } | 449 | } |
453 | case OP_FORLOOP: | 450 | case OP_FORLOOP: |
454 | case OP_LFORLOOP: | 451 | case OP_TFORLOOP: { |
455 | case OP_JMPONT: | 452 | int dest = pc+b; |
456 | case OP_JMPONF: { | 453 | check(0 <= dest && dest < pt->sizecode && |
457 | int newpc = pc+arg1; | 454 | pt->code[dest] == SET_OPCODE(i, op-1)); |
458 | checkjump(pt, sl, top, newpc); | 455 | checkreg(pt, a + ((op == OP_FORLOOP) ? 2 : 3)); |
459 | /* jump is forward and do not skip `lastpc' and not full check? */ | ||
460 | if (pc < newpc && newpc <= lastpc && stackpos >= 0) { | ||
461 | stack[top-1] = pc-1; /* value comes from `and'/`or' */ | ||
462 | pc = newpc; /* do the jump */ | ||
463 | pop = 0; /* do not pop */ | ||
464 | } | ||
465 | break; | ||
466 | } | ||
467 | case OP_PUSHNILJMP: { | ||
468 | check(GET_OPCODE(pt->code[pc]) == OP_PUSHINT); /* only valid sequence */ | ||
469 | break; | 456 | break; |
470 | } | 457 | } |
471 | case OP_FORPREP: { | 458 | case OP_SETLIST: { |
472 | int endfor = pc-arg1-1; /* jump is `negative' here */ | 459 | checkreg(pt, a + (b&(LFIELDS_PER_FLUSH-1)) + 1); |
473 | check(top >= 3); | ||
474 | checkjump(pt, sl, top+push, endfor); | ||
475 | check(GET_OPCODE(pt->code[endfor]) == OP_FORLOOP); | ||
476 | check(GETARG_S(pt->code[endfor]) == arg1); | ||
477 | break; | ||
478 | } | ||
479 | case OP_LFORPREP: { | ||
480 | int endfor = pc-arg1-1; /* jump is `negative' here */ | ||
481 | check(top >= 1); | ||
482 | checkjump(pt, sl, top+push, endfor); | ||
483 | check(GET_OPCODE(pt->code[endfor]) == OP_LFORLOOP); | ||
484 | check(GETARG_S(pt->code[endfor]) == arg1); | ||
485 | break; | 460 | break; |
486 | } | 461 | } |
487 | case OP_PUSHINT: | 462 | case OP_CLOSURE: { |
488 | case OP_GETTABLE: | 463 | check(b < pt->sizekproto); |
489 | case OP_CREATETABLE: | 464 | checkreg(pt, a + pt->kproto[b]->nupvalues - 1); |
490 | case OP_ADD: | ||
491 | case OP_ADDI: | ||
492 | case OP_SUB: | ||
493 | case OP_MULT: | ||
494 | case OP_DIV: | ||
495 | case OP_POW: | ||
496 | case OP_MINUS: | ||
497 | case OP_NOT: { | ||
498 | break; | 465 | break; |
499 | } | 466 | } |
467 | default: break; | ||
500 | } | 468 | } |
501 | top -= pop; | ||
502 | check(0 <= top && top+push <= pt->maxstacksize); | ||
503 | while (push--) stack[top++] = pc-1; | ||
504 | checkjump(pt, sl, top, pc); | ||
505 | } | 469 | } |
506 | return (stackpos >= 0) ? pt->code[stack[stackpos]] : 1; | 470 | return pt->code[last]; |
507 | } | 471 | } |
508 | 472 | ||
509 | /* }====================================================== */ | 473 | /* }====================================================== */ |
510 | 474 | ||
511 | 475 | ||
512 | int luaG_checkcode (lua_State *L, const Proto *pt) { | 476 | int luaG_checkcode (const Proto *pt) { |
513 | return luaG_symbexec(L, pt, pt->sizecode-1, -1); | 477 | return luaG_symbexec(pt, pt->sizecode, NO_REG); |
514 | } | 478 | } |
515 | 479 | ||
516 | 480 | ||
517 | static const l_char *getobjname (lua_State *L, StkId obj, const l_char **name) { | 481 | static const l_char *getobjname (lua_State *L, StkId obj, const l_char **name) { |
518 | CallInfo *ci = ci_stack(L, obj); | 482 | CallInfo *ci = ci_stack(L, obj); |
519 | if (!isLmark(ci)) | 483 | if (isLmark(ci)) { /* an active Lua function? */ |
520 | return NULL; /* not an active Lua function */ | ||
521 | else { | ||
522 | Proto *p = ci_func(ci)->f.l; | 484 | Proto *p = ci_func(ci)->f.l; |
523 | int pc = currentpc(ci); | 485 | int pc = currentpc(ci); |
524 | int stackpos = obj - ci->base; | 486 | int stackpos = obj - ci->base; |
525 | Instruction i = luaG_symbexec(L, p, pc, stackpos); | 487 | Instruction i; |
488 | *name = luaF_getlocalname(p, stackpos+1, pc); | ||
489 | if (*name) /* is a local? */ | ||
490 | return l_s("local"); | ||
491 | i = luaG_symbexec(p, pc, stackpos); /* try symbolic execution */ | ||
526 | lua_assert(pc != -1); | 492 | lua_assert(pc != -1); |
527 | switch (GET_OPCODE(i)) { | 493 | switch (GET_OPCODE(i)) { |
528 | case OP_GETGLOBAL: { | 494 | case OP_GETGLOBAL: { |
529 | *name = getstr(p->kstr[GETARG_U(i)]); | 495 | lua_assert(ttype(&p->k[GETARG_Bc(i)]) == LUA_TSTRING); |
496 | *name = getstr(tsvalue(&p->k[GETARG_Bc(i)])); | ||
530 | return l_s("global"); | 497 | return l_s("global"); |
531 | } | 498 | } |
532 | case OP_GETLOCAL: { | 499 | case OP_MOVE: { |
533 | *name = luaF_getlocalname(p, GETARG_U(i)+1, pc); | 500 | int a = GETARG_A(i); |
534 | lua_assert(*name); | 501 | int b = GETARG_B(i); /* move from `b' to `a' */ |
535 | return l_s("local"); | 502 | if (b < a) |
503 | return getobjname(L, ci->base+b, name); /* get name for `b' */ | ||
504 | break; | ||
536 | } | 505 | } |
537 | case OP_PUSHSELF: | 506 | case OP_GETTABLE: |
538 | case OP_GETDOTTED: { | 507 | case OP_SELF: { |
539 | *name = getstr(p->kstr[GETARG_U(i)]); | 508 | int c = GETARG_C(i) - MAXSTACK; |
540 | return l_s("field"); | 509 | if (c >= 0 && ttype(&p->k[c]) == LUA_TSTRING) { |
510 | *name = getstr(tsvalue(&p->k[c])); | ||
511 | return l_s("field"); | ||
512 | } | ||
513 | break; | ||
541 | } | 514 | } |
542 | default: | 515 | default: break; |
543 | return NULL; /* no useful name found */ | ||
544 | } | 516 | } |
545 | } | 517 | } |
518 | return NULL; /* no useful name found */ | ||
546 | } | 519 | } |
547 | 520 | ||
548 | 521 | ||
@@ -576,10 +549,18 @@ void luaG_typeerror (lua_State *L, StkId o, const l_char *op) { | |||
576 | } | 549 | } |
577 | 550 | ||
578 | 551 | ||
579 | void luaG_binerror (lua_State *L, StkId p1, int t, const l_char *op) { | 552 | void luaG_concaterror (lua_State *L, StkId p1, StkId p2) { |
580 | if (ttype(p1) == t) p1++; | 553 | if (ttype(p1) == LUA_TSTRING) p1 = p2; |
581 | lua_assert(ttype(p1) != t); | 554 | lua_assert(ttype(p1) != LUA_TSTRING); |
582 | luaG_typeerror(L, p1, op); | 555 | luaG_typeerror(L, p1, l_s("concat")); |
556 | } | ||
557 | |||
558 | |||
559 | void luaG_aritherror (lua_State *L, StkId p1, TObject *p2) { | ||
560 | TObject temp; | ||
561 | if (luaV_tonumber(p1, &temp) != NULL) | ||
562 | p1 = p2; /* first operand is OK; error is in the second */ | ||
563 | luaG_typeerror(L, p1, l_s("perform arithmetic on")); | ||
583 | } | 564 | } |
584 | 565 | ||
585 | 566 | ||
@@ -592,3 +573,52 @@ void luaG_ordererror (lua_State *L, const TObject *p1, const TObject *p2) { | |||
592 | luaO_verror(L, l_s("attempt to compare %.10s with %.10s"), t1, t2); | 573 | luaO_verror(L, l_s("attempt to compare %.10s with %.10s"), t1, t2); |
593 | } | 574 | } |
594 | 575 | ||
576 | |||
577 | |||
578 | #define opmode(t,a,b,c,sa,k,m) (((t)<<OpModeT) | \ | ||
579 | ((a)<<OpModeAreg) | ((b)<<OpModeBreg) | ((c)<<OpModeCreg) | \ | ||
580 | ((sa)<<OpModesetA) | ((k)<<OpModeK) | (m)) | ||
581 | |||
582 | |||
583 | const unsigned char luaG_opmodes[] = { | ||
584 | /* T A B C sA K mode opcode */ | ||
585 | opmode(0,1,1,0, 1,0,iABC), /* OP_MOVE */ | ||
586 | opmode(0,1,0,0, 1,1,iABc), /* OP_LOADK */ | ||
587 | opmode(0,1,0,0, 1,0,iAsBc), /* OP_LOADINT */ | ||
588 | opmode(0,1,1,0, 1,0,iABC), /* OP_LOADNIL */ | ||
589 | opmode(0,1,0,0, 1,0,iABc), /* OP_LOADUPVAL */ | ||
590 | opmode(0,1,0,0, 1,1,iABc), /* OP_GETGLOBAL */ | ||
591 | opmode(0,1,1,1, 1,0,iABC), /* OP_GETTABLE */ | ||
592 | opmode(0,1,0,0, 0,1,iABc), /* OP_SETGLOBAL */ | ||
593 | opmode(0,1,1,1, 0,0,iABC), /* OP_SETTABLE */ | ||
594 | opmode(0,1,0,0, 1,0,iABc), /* OP_NEWTABLE */ | ||
595 | opmode(0,1,1,1, 1,0,iABC), /* OP_SELF */ | ||
596 | opmode(0,1,1,1, 1,0,iABC), /* OP_ADD */ | ||
597 | opmode(0,1,1,1, 1,0,iABC), /* OP_SUB */ | ||
598 | opmode(0,1,1,1, 1,0,iABC), /* OP_MUL */ | ||
599 | opmode(0,1,1,1, 1,0,iABC), /* OP_DIV */ | ||
600 | opmode(0,1,1,1, 1,0,iABC), /* OP_POW */ | ||
601 | opmode(0,1,1,0, 1,0,iABC), /* OP_UNM */ | ||
602 | opmode(0,1,1,0, 1,0,iABC), /* OP_NOT */ | ||
603 | opmode(0,1,1,1, 1,0,iABC), /* OP_CONCAT */ | ||
604 | opmode(0,0,0,0, 0,0,iAsBc), /* OP_JMP */ | ||
605 | opmode(0,0,0,0, 0,0,iAsBc), /* OP_CJMP */ | ||
606 | opmode(1,0,1,1, 0,0,iABC), /* OP_TESTEQ */ | ||
607 | opmode(1,0,1,1, 0,0,iABC), /* OP_TESTNE */ | ||
608 | opmode(1,0,1,1, 0,0,iABC), /* OP_TESTLT */ | ||
609 | opmode(1,0,1,1, 0,0,iABC), /* OP_TESTLE */ | ||
610 | opmode(1,0,1,1, 0,0,iABC), /* OP_TESTGT */ | ||
611 | opmode(1,0,1,1, 0,0,iABC), /* OP_TESTGE */ | ||
612 | opmode(1,0,1,0, 1,0,iABC), /* OP_TESTT */ | ||
613 | opmode(1,0,1,0, 1,0,iABC), /* OP_TESTF */ | ||
614 | opmode(0,1,0,0, 1,0,iAsBc), /* OP_NILJMP */ | ||
615 | opmode(0,1,0,0, 0,0,iABC), /* OP_CALL */ | ||
616 | opmode(0,1,0,0, 0,0,iABC), /* OP_RETURN */ | ||
617 | opmode(0,1,0,0, 0,0,iAsBc), /* OP_FORPREP */ | ||
618 | opmode(0,1,0,0, 0,0,iAsBc), /* OP_FORLOOP */ | ||
619 | opmode(0,1,0,0, 0,0,iAsBc), /* OP_TFORPREP */ | ||
620 | opmode(0,1,0,0, 0,0,iAsBc), /* OP_TFORLOOP */ | ||
621 | opmode(0,1,0,0, 0,0,iABc), /* OP_SETLIST */ | ||
622 | opmode(0,1,0,0, 0,0,iABc), /* OP_SETLIST0 */ | ||
623 | opmode(0,1,0,0, 0,0,iABc) /* OP_CLOSURE */ | ||
624 | }; | ||