/* ** Call a C function. CLS_current.base will point to the top of the stack, ** and CLS_current.num is the number of parameters. Returns an index ** to the first result from C. */ static StkId callC (lua_CFunction func, StkId base) { struct C_Lua_Stack oldCLS = CLS_current; StkId firstResult; CLS_current.num = (top-stack) - base; /* incorporate parameters on the stack */ CLS_current.base = base+CLS_current.num; /* == top-stack */ if (lua_callhook) callHook(base, LUA_T_CMARK, 0); (*func)(); if (lua_callhook) /* func may have changed lua_callhook */ callHook(base, LUA_T_CMARK, 1); firstResult = CLS_current.base; CLS_current = oldCLS; return firstResult; }
void callEpilogue(Thread* t) { if(t->hooks & CrocThreadHook_Ret) callHook(t, CrocThreadHook_Ret); // Get results before popARTo takes them away auto destSlot = t->currentAR->returnSlot; auto expectedResults = t->currentAR->expectedResults; auto results = loadResults(t); // Pop the act record (which also closes upvals and removes EH frames) popARTo(t, t->arIndex - 1); // Copy and adjust results bool isMultRet = expectedResults == -1; auto actualResults = results.length; if(isMultRet) expectedResults = actualResults; auto stk = t->stack; auto slotAfterRets = destSlot + expectedResults; if(cast(uword)expectedResults <= actualResults) stk.slicea(destSlot, slotAfterRets, results.slice(0, expectedResults)); else { stk.slicea(destSlot, destSlot + actualResults, results); stk.slice(destSlot + actualResults, slotAfterRets).fill(Value::nullValue); } t->numYields = actualResults; // Set stack index appropriately; last case happens in native -> native calls if(t->arIndex == 0 || isMultRet || t->currentAR->savedTop < slotAfterRets) t->stackIndex = slotAfterRets; else t->stackIndex = t->currentAR->savedTop; assert(t->stackIndex > 0); }
bool funcCallPrologue(Thread* t, Function* func, AbsStack returnSlot, word expectedResults, AbsStack paramSlot, uword numParams, bool isTailcall) { if(numParams > func->maxParams) croc_eh_throwStd(*t, "ParamError", "Function %s expected at most %" CROC_SIZE_T_FORMAT " parameters but was given %" CROC_SIZE_T_FORMAT, func->name->toCString(), func->maxParams - 1, numParams - 1); if(!func->isNative) { // Script function auto funcdef = func->scriptFunc; auto ar = isTailcall ? t->currentAR : pushAR(t); if(isTailcall) { assert(ar && ar->func && !ar->func->isNative); assert(paramSlot == returnSlot + 1); closeUpvals(t, ar->base); ar->numTailcalls++; memmove(&t->stack[ar->returnSlot], &t->stack[returnSlot], sizeof(Value) * (numParams + 1)); returnSlot = ar->returnSlot; paramSlot = returnSlot + 1; } if(funcdef->isVararg && numParams > func->numParams) { // In this case, we move the formal parameters after the varargs and null out where the formal // params used to be. ar->base = paramSlot + numParams; ar->vargBase = paramSlot + func->numParams; checkStack(t, ar->base + funcdef->stackSize - 1); auto oldParams = t->stack.slice(paramSlot, paramSlot + func->numParams); t->stack.slicea(ar->base, ar->base + func->numParams, oldParams); oldParams.fill(Value::nullValue); // For nulling out the stack. numParams = func->numParams; } else { // In this case, everything is where it needs to be already. ar->base = paramSlot; ar->vargBase = paramSlot; checkStack(t, ar->base + funcdef->stackSize - 1); // If we have too few params, the extra param slots will be nulled out. } // Null out the stack frame after the parameters. t->stack.slice(ar->base + numParams, ar->base + funcdef->stackSize).fill(Value::nullValue); // Fill in the rest of the activation record. ar->returnSlot = returnSlot; ar->func = func; ar->pc = funcdef->code.ptr; ar->firstResult = 0; ar->numResults = 0; ar->savedTop = ar->base + funcdef->stackSize; ar->unwindCounter = 0; ar->unwindReturn = nullptr; if(!isTailcall) { ar->expectedResults = expectedResults; ar->numTailcalls = 0; } // Set the stack indices. t->stackBase = ar->base; t->stackIndex = ar->savedTop; // Call any hook. if(t->hooks & CrocThreadHook_Call) callHook(t, isTailcall ? CrocThreadHook_TailCall : CrocThreadHook_Call); return true; } else { // Native function t->stackIndex = paramSlot + numParams; checkStack(t, t->stackIndex); auto ar = pushAR(t); ar->base = paramSlot; ar->vargBase = paramSlot; ar->returnSlot = returnSlot; ar->func = func; ar->expectedResults = expectedResults; ar->firstResult = 0; ar->numResults = 0; ar->savedTop = t->stackIndex; ar->numTailcalls = 0; ar->unwindCounter = 0; ar->unwindReturn = nullptr; t->stackBase = ar->base; if(t->hooks & CrocThreadHook_Call) callHook(t, CrocThreadHook_Call); t->vm->curThread = t; t->nativeCallDepth++; auto savedState = t->state; t->state = CrocThreadState_Running; word actualResults = func->nativeFunc(*t); t->state = savedState; t->nativeCallDepth--; assert(actualResults >= 0); // for now, will change in the future if(cast(uword)actualResults > (t->stackIndex - (t->stackBase + 1))) croc_eh_throwStd(*t, "ApiError", "Native function '%s' returned an invalid number of results", func->name->toCString()); saveResults(t, t, t->stackIndex - actualResults, actualResults); callEpilogue(t); return false; } }
/* ** Execute the given opcode, until a RET. Parameters are between ** [stack+base,top). Returns n such that the the results are between ** [stack+n,top). */ static StkId lua_execute (Byte *pc, StkId base) { void* table[] = { &&pushnil, &&push0, &&push1, &&push2, &&pushbyte, &&pushword, &&pushfloat, &&pushstring, &&pushfunction, &&pushlocal0, &&pushlocal1, &&pushlocal2, &&pushlocal3, &&pushlocal4, &&pushlocal5, &&pushlocal6, &&pushlocal7, &&pushlocal8, &&pushlocal9, &&pushlocal, &&pushglobal, &&pushindexed, &&pushself, &&storelocal0, &&storelocal1, &&storelocal2, &&storelocal3, &&storelocal4, &&storelocal5, &&storelocal6, &&storelocal7, &&storelocal8, &&storelocal9, &&storelocal, &&storeglobal, &&storeindexed0, &&storeindexed, &&storelist0, &&storelist, &&storerecord, &&adjust0, &&adjust, &&createarray, &&eqop, &<op, &&leop, &>op, &&geop, &&addop, &&subop, &&multop, &&divop, &&powop, &&concop, &&minusop, &¬op, &&ontjmp, &&onfjmp, &&jmp, &&upjmp, &&iffjmp, &&iffupjmp, &&pop, &&callfunc, &&retcode0, &&retcode, &&setline, &&varargs }; if (lua_callhook) callHook (base, LUA_T_MARK, 0); goto *table[*pc++]; pushnil: tag(top) = LUA_T_NIL; incr_top; goto *table[*pc++]; push0: push1: push2: tag(top) = LUA_T_NUMBER; nvalue(top) = ((OpCode)*(pc-1))-PUSH0; incr_top; goto *table[*pc++]; pushbyte: tag(top) = LUA_T_NUMBER; nvalue(top) = *pc++; incr_top; goto *table[*pc++]; pushword: { Word w; get_word(w,pc); tag(top) = LUA_T_NUMBER; nvalue(top) = w; incr_top; } goto *table[*pc++]; pushfloat: { real num; get_float(num,pc); tag(top) = LUA_T_NUMBER; nvalue(top) = num; incr_top; } goto *table[*pc++]; pushstring: { Word w; get_word(w,pc); tag(top) = LUA_T_STRING; tsvalue(top) = lua_constant[w]; incr_top; } goto *table[*pc++]; pushfunction: { TFunc *f; get_code(f,pc); luaI_insertfunction(f); /* may take part in GC */ top->tag = LUA_T_FUNCTION; top->value.tf = f; incr_top; } goto *table[*pc++]; pushlocal0: pushlocal1: pushlocal2: pushlocal3: pushlocal4: pushlocal5: pushlocal6: pushlocal7: pushlocal8: pushlocal9: *top = *((stack+base) + (int)(((OpCode)*(pc-1))-PUSHLOCAL0)); incr_top; goto *table[*pc++]; pushlocal: *top = *((stack+base) + (*pc++)); incr_top; goto *table[*pc++]; pushglobal: { Word w; get_word(w,pc); getglobal(w); } goto *table[*pc++]; pushindexed: pushsubscript(); goto *table[*pc++]; pushself: { Object receiver = *(top-1); Word w; get_word(w,pc); tag(top) = LUA_T_STRING; tsvalue(top) = lua_constant[w]; incr_top; pushsubscript(); *top = receiver; incr_top; goto *table[*pc++]; } storelocal0: storelocal1: storelocal2: storelocal3: storelocal4: storelocal5: storelocal6: storelocal7: storelocal8: storelocal9: *((stack+base) + (int)(((OpCode)*(pc-1))-STORELOCAL0)) = *(--top); goto *table[*pc++]; storelocal: *((stack+base) + (*pc++)) = *(--top); goto *table[*pc++]; storeglobal: { Word w; get_word(w,pc); s_object(w) = *(--top); } goto *table[*pc++]; storeindexed0: storesubscript(); goto *table[*pc++]; storeindexed: { int n = *pc++; if (tag(top-3-n) != LUA_T_ARRAY) { lua_checkstack(top+2); *(top+1) = *(top-1); *(top) = *(top-2-n); *(top-1) = *(top-3-n); top += 2; callFB(FB_SETTABLE); } else { Object *h = lua_hashdefine (avalue(top-3-n), top-2-n); *h = *(top-1); top--; } } goto *table[*pc++]; storelist0: storelist: { int m, n; Object *arr; if (((OpCode)*(pc-1)) == STORELIST0) m = 0; else m = *(pc++) * FIELDS_PER_FLUSH; n = *(pc++); arr = top-n-1; while (n) { tag(top) = LUA_T_NUMBER; nvalue(top) = n+m; *(lua_hashdefine (avalue(arr), top)) = *(top-1); top--; n--; } } goto *table[*pc++]; storerecord: { int n = *(pc++); Object *arr = top-n-1; while (n) { Word w; get_word(w,pc); tag(top) = LUA_T_STRING; tsvalue(top) = lua_constant[w]; *(lua_hashdefine (avalue(arr), top)) = *(top-1); top--; n--; } } goto *table[*pc++]; adjust0: adjust_top(base); goto *table[*pc++]; adjust: adjust_top(base + *(pc++)); goto *table[*pc++]; varargs: adjust_varargs(base + *(pc++)); goto *table[*pc++]; createarray: { Word size; get_word(size,pc); avalue(top) = lua_createarray(size); tag(top) = LUA_T_ARRAY; incr_top; } goto *table[*pc++]; eqop: { int res = lua_equalObj(top-2, top-1); --top; tag(top-1) = res ? LUA_T_NUMBER : LUA_T_NIL; nvalue(top-1) = 1; } goto *table[*pc++]; ltop: comparison(LUA_T_NUMBER, LUA_T_NIL, LUA_T_NIL, "lt"); goto *table[*pc++]; leop: comparison(LUA_T_NUMBER, LUA_T_NUMBER, LUA_T_NIL, "le"); goto *table[*pc++]; gtop: comparison(LUA_T_NIL, LUA_T_NIL, LUA_T_NUMBER, "gt"); goto *table[*pc++]; geop: comparison(LUA_T_NIL, LUA_T_NUMBER, LUA_T_NUMBER, "ge"); goto *table[*pc++]; addop: { Object *l = top-2; Object *r = top-1; if (tonumber(r) || tonumber(l)) call_arith("add"); else { nvalue(l) += nvalue(r); --top; } } goto *table[*pc++]; subop: { Object *l = top-2; Object *r = top-1; if (tonumber(r) || tonumber(l)) call_arith("sub"); else { nvalue(l) -= nvalue(r); --top; } } goto *table[*pc++]; multop: { Object *l = top-2; Object *r = top-1; if (tonumber(r) || tonumber(l)) call_arith("mul"); else { nvalue(l) *= nvalue(r); --top; } } goto *table[*pc++]; divop: { Object *l = top-2; Object *r = top-1; if (tonumber(r) || tonumber(l)) call_arith("div"); else { nvalue(l) /= nvalue(r); --top; } } goto *table[*pc++]; powop: call_arith("pow"); goto *table[*pc++]; concop: { Object *l = top-2; Object *r = top-1; if (tostring(r) || tostring(l)) callFB(FB_CONCAT); else { tsvalue(l) = lua_createstring (lua_strconc(svalue(l),svalue(r))); --top; } } goto *table[*pc++]; minusop: if (tonumber(top-1)) { tag(top) = LUA_T_NIL; incr_top; call_arith("unm"); } else nvalue(top-1) = - nvalue(top-1); goto *table[*pc++]; notop: tag(top-1) = (tag(top-1) == LUA_T_NIL) ? LUA_T_NUMBER : LUA_T_NIL; nvalue(top-1) = 1; goto *table[*pc++]; ontjmp: { Word w; get_word(w,pc); if (tag(top-1) != LUA_T_NIL) pc += w; } goto *table[*pc++]; onfjmp: { Word w; get_word(w,pc); if (tag(top-1) == LUA_T_NIL) pc += w; } goto *table[*pc++]; jmp: { Word w; get_word(w,pc); pc += w; } goto *table[*pc++]; upjmp: { Word w; get_word(w,pc); pc -= w; } goto *table[*pc++]; iffjmp: { Word w; get_word(w,pc); top--; if (tag(top) == LUA_T_NIL) pc += w; } goto *table[*pc++]; iffupjmp: { Word w; get_word(w,pc); top--; if (tag(top) == LUA_T_NIL) pc -= w; } goto *table[*pc++]; pop: --top; goto *table[*pc++]; callfunc: { int nParams = *(pc++); int nResults = *(pc++); StkId newBase = (top-stack)-nParams; do_call(newBase, nResults); } goto *table[*pc++]; retcode0: retcode: if (lua_callhook) callHook (base, LUA_T_MARK, 1); return (base + ((((OpCode)*(pc-1))==RETCODE0) ? 0 : *pc)); setline: { Word line; get_word(line,pc); if ((stack+base-1)->tag != LUA_T_LINE) { /* open space for LINE value */ open_stack((top-stack)-base); base++; (stack+base-1)->tag = LUA_T_LINE; } (stack+base-1)->value.i = line; if (lua_linehook) lineHook (line); goto *table[*pc++]; } lua_error ("internal error - opcode doesn't match"); }
/* ** Execute the given opcode, until a RET. Parameters are between ** [stack+base,top). Returns n such that the the results are between ** [stack+n,top). */ static StkId lua_execute (Byte *pc, StkId base) { if (lua_callhook) callHook (base, LUA_T_MARK, 0); while (1) { OpCode opcode; switch (opcode = (OpCode)*pc++) { case PUSHNIL: ttype(top) = LUA_T_NIL; incr_top; break; case PUSH0: case PUSH1: case PUSH2: ttype(top) = LUA_T_NUMBER; nvalue(top) = opcode-PUSH0; incr_top; break; case PUSHBYTE: ttype(top) = LUA_T_NUMBER; nvalue(top) = *pc++; incr_top; break; case PUSHWORD: { Word w; get_word(w,pc); ttype(top) = LUA_T_NUMBER; nvalue(top) = w; incr_top; } break; case PUSHFLOAT: { real num; get_float(num,pc); ttype(top) = LUA_T_NUMBER; nvalue(top) = num; incr_top; } break; case PUSHSTRING: { Word w; get_word(w,pc); ttype(top) = LUA_T_STRING; tsvalue(top) = lua_constant[w]; incr_top; } break; case PUSHFUNCTION: { TFunc *f; get_code(f,pc); luaI_insertfunction(f); /* may take part in GC */ top->ttype = LUA_T_FUNCTION; top->value.tf = f; incr_top; } break; case PUSHLOCAL0: case PUSHLOCAL1: case PUSHLOCAL2: case PUSHLOCAL3: case PUSHLOCAL4: case PUSHLOCAL5: case PUSHLOCAL6: case PUSHLOCAL7: case PUSHLOCAL8: case PUSHLOCAL9: *top = *((stack+base) + (int)(opcode-PUSHLOCAL0)); incr_top; break; case PUSHLOCAL: *top = *((stack+base) + (*pc++)); incr_top; break; case PUSHGLOBAL: { Word w; get_word(w,pc); getglobal(w); } break; case PUSHINDEXED: pushsubscript(); break; case PUSHSELF: { TObject receiver = *(top-1); Word w; get_word(w,pc); ttype(top) = LUA_T_STRING; tsvalue(top) = lua_constant[w]; incr_top; pushsubscript(); *top = receiver; incr_top; break; } case STORELOCAL0: case STORELOCAL1: case STORELOCAL2: case STORELOCAL3: case STORELOCAL4: case STORELOCAL5: case STORELOCAL6: case STORELOCAL7: case STORELOCAL8: case STORELOCAL9: *((stack+base) + (int)(opcode-STORELOCAL0)) = *(--top); break; case STORELOCAL: *((stack+base) + (*pc++)) = *(--top); break; case STOREGLOBAL: { Word w; get_word(w,pc); setglobal(w); } break; case STOREINDEXED0: storesubscript(top-3, 1); break; case STOREINDEXED: { int n = *pc++; storesubscript(top-3-n, 2); break; } case STORELIST0: case STORELIST: { int m, n; TObject *arr; if (opcode == STORELIST0) m = 0; else m = *(pc++) * FIELDS_PER_FLUSH; n = *(pc++); arr = top-n-1; while (n) { ttype(top) = LUA_T_NUMBER; nvalue(top) = n+m; *(lua_hashdefine (avalue(arr), top)) = *(top-1); top--; n--; } } break; case STORERECORD: /* opcode obsolete: supersed by STOREMAP */ { int n = *(pc++); TObject *arr = top-n-1; while (n) { Word w; get_word(w,pc); ttype(top) = LUA_T_STRING; tsvalue(top) = lua_constant[w]; *(lua_hashdefine (avalue(arr), top)) = *(top-1); top--; n--; } } break; case STOREMAP: { int n = *(pc++); TObject *arr = top-(2*n)-1; while (n--) { *(lua_hashdefine (avalue(arr), top-2)) = *(top-1); top-=2; } } break; case ADJUST0: adjust_top(base); break; case ADJUST: { StkId newtop = base + *(pc++); adjust_top(newtop); break; } case VARARGS: adjust_varargs(base + *(pc++)); break; case CREATEARRAY: { Word size; get_word(size,pc); avalue(top) = lua_createarray(size); ttype(top) = LUA_T_ARRAY; incr_top; } break; case EQOP: { int res = lua_equalObj(top-2, top-1); --top; ttype(top-1) = res ? LUA_T_NUMBER : LUA_T_NIL; nvalue(top-1) = 1; } break; case LTOP: comparison(LUA_T_NUMBER, LUA_T_NIL, LUA_T_NIL, IM_LT); break; case LEOP: comparison(LUA_T_NUMBER, LUA_T_NUMBER, LUA_T_NIL, IM_LE); break; case GTOP: comparison(LUA_T_NIL, LUA_T_NIL, LUA_T_NUMBER, IM_GT); break; case GEOP: comparison(LUA_T_NIL, LUA_T_NUMBER, LUA_T_NUMBER, IM_GE); break; case ADDOP: { TObject *l = top-2; TObject *r = top-1; if (tonumber(r) || tonumber(l)) call_arith(IM_ADD); else { nvalue(l) += nvalue(r); --top; } } break; case SUBOP: { TObject *l = top-2; TObject *r = top-1; if (tonumber(r) || tonumber(l)) call_arith(IM_SUB); else { nvalue(l) -= nvalue(r); --top; } } break; case MULTOP: { TObject *l = top-2; TObject *r = top-1; if (tonumber(r) || tonumber(l)) call_arith(IM_MUL); else { nvalue(l) *= nvalue(r); --top; } } break; case DIVOP: { TObject *l = top-2; TObject *r = top-1; if (tonumber(r) || tonumber(l)) call_arith(IM_DIV); else { nvalue(l) /= nvalue(r); --top; } } break; case POWOP: call_arith(IM_POW); break; case CONCOP: { TObject *l = top-2; TObject *r = top-1; if (tostring(l) || tostring(r)) call_binTM(IM_CONCAT, "unexpected type for concatenation"); else { tsvalue(l) = lua_createstring(lua_strconc(svalue(l),svalue(r))); --top; } } break; case MINUSOP: if (tonumber(top-1)) { ttype(top) = LUA_T_NIL; incr_top; call_arith(IM_UNM); } else nvalue(top-1) = - nvalue(top-1); break; case NOTOP: ttype(top-1) = (ttype(top-1) == LUA_T_NIL) ? LUA_T_NUMBER : LUA_T_NIL; nvalue(top-1) = 1; break; case ONTJMP: { Word w; get_word(w,pc); if (ttype(top-1) != LUA_T_NIL) pc += w; } break; case ONFJMP: { Word w; get_word(w,pc); if (ttype(top-1) == LUA_T_NIL) pc += w; } break; case JMP: { Word w; get_word(w,pc); pc += w; } break; case UPJMP: { Word w; get_word(w,pc); pc -= w; } break; case IFFJMP: { Word w; get_word(w,pc); top--; if (ttype(top) == LUA_T_NIL) pc += w; } break; case IFFUPJMP: { Word w; get_word(w,pc); top--; if (ttype(top) == LUA_T_NIL) pc -= w; } break; case POP: --top; break; case CALLFUNC: { int nParams = *(pc++); int nResults = *(pc++); StkId newBase = (top-stack)-nParams; do_call(newBase, nResults); } break; case RETCODE0: case RETCODE: if (lua_callhook) callHook (base, LUA_T_MARK, 1); return (base + ((opcode==RETCODE0) ? 0 : *pc)); case SETLINE: { Word line; get_word(line,pc); if ((stack+base-1)->ttype != LUA_T_LINE) { /* open space for LINE value */ open_stack((top-stack)-base); base++; (stack+base-1)->ttype = LUA_T_LINE; } (stack+base-1)->value.i = line; if (lua_linehook) lineHook (line); break; } default: lua_error ("internal error - opcode doesn't match"); } } }