1
0
forked from Mirror/wren

Track the maximum number of slots each function may use.

This is the first step towards dynamically grown stacks. We don't want
to check for stack overflow on every single push, so we store the max
number of slots a function might need and (in later patches) ensure
at function call time that that many slots are available.
This commit is contained in:
Bob Nystrom
2015-12-13 22:57:40 -08:00
parent 2df5362ab2
commit c7cd1fb1ac
7 changed files with 179 additions and 125 deletions

View File

@ -322,6 +322,13 @@ struct sCompiler
// here means top-level code is being compiled and there is no block scope
// in effect at all. Any variables declared will be module-level.
int scopeDepth;
// The current number of slots (locals and temporaries) in use.
int numSlots;
// The maximum number of slots (locals and temporaries) in use at one time in
// the function.
int maxSlots;
// The current innermost loop being compiled, or NULL if not in a loop.
Loop* loop;
@ -336,6 +343,14 @@ struct sCompiler
IntBuffer debugSourceLines;
};
// The stack effect of each opcode. The index in the array is the opcode, and
// the value is the stack effect of that instruction.
static const int stackEffects[] = {
#define OPCODE(_, effect) effect,
#include "wren_opcodes.h"
#undef OPCODE
};
// Outputs a compile or syntax error. This also marks the compilation as having
// an error, which ensures that the resulting code will be discarded and never
// run. This means that after calling lexError(), it's fine to generate whatever
@ -469,6 +484,9 @@ static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent,
// The initial scope for function or method is a local scope.
compiler->scopeDepth = 0;
}
compiler->numSlots = compiler->numLocals;
compiler->maxSlots = compiler->numLocals;
wrenByteBufferInit(&compiler->bytecode);
wrenIntBufferInit(&compiler->debugSourceLines);
@ -1063,38 +1081,51 @@ static void consumeLine(Compiler* compiler, const char* errorMessage)
// Variables and scopes --------------------------------------------------------
// Emits one bytecode instruction or single-byte argument. Returns its index.
static int emit(Compiler* compiler, int byte)
// Emits one single-byte argument. Returns its index.
static int emitByte(Compiler* compiler, int byte)
{
wrenByteBufferWrite(compiler->parser->vm, &compiler->bytecode, (uint8_t)byte);
// Assume the instruction is associated with the most recently consumed token.
wrenIntBufferWrite(compiler->parser->vm, &compiler->debugSourceLines,
compiler->parser->previous.line);
compiler->parser->previous.line);
return compiler->bytecode.count - 1;
}
// Emits one bytecode instruction.
static void emitOp(Compiler* compiler, Code instruction)
{
emitByte(compiler, instruction);
// Keep track of the stack's high water mark.
compiler->numSlots += stackEffects[instruction];
if (compiler->numSlots > compiler->maxSlots)
{
compiler->maxSlots = compiler->numSlots;
}
}
// Emits one 16-bit argument, which will be written big endian.
static void emitShort(Compiler* compiler, int arg)
{
emit(compiler, (arg >> 8) & 0xff);
emit(compiler, arg & 0xff);
emitByte(compiler, (arg >> 8) & 0xff);
emitByte(compiler, arg & 0xff);
}
// Emits one bytecode instruction followed by a 8-bit argument. Returns the
// index of the argument in the bytecode.
static int emitByteArg(Compiler* compiler, Code instruction, int arg)
{
emit(compiler, instruction);
return emit(compiler, arg);
emitOp(compiler, instruction);
return emitByte(compiler, arg);
}
// Emits one bytecode instruction followed by a 16-bit argument, which will be
// written big endian.
static void emitShortArg(Compiler* compiler, Code instruction, int arg)
{
emit(compiler, instruction);
emitOp(compiler, instruction);
emitShort(compiler, arg);
}
@ -1103,9 +1134,9 @@ static void emitShortArg(Compiler* compiler, Code instruction, int arg)
// placeholder.
static int emitJump(Compiler* compiler, Code instruction)
{
emit(compiler, instruction);
emit(compiler, 0xff);
return emit(compiler, 0xff) - 1;
emitOp(compiler, instruction);
emitByte(compiler, 0xff);
return emitByte(compiler, 0xff) - 1;
}
// Creates a new constant for the current value and emits the bytecode to load
@ -1207,7 +1238,7 @@ static void defineVariable(Compiler* compiler, int symbol)
// It's a module-level variable, so store the value in the module slot and
// then discard the temporary for the initializer.
emitShortArg(compiler, CODE_STORE_MODULE_VAR, symbol);
emit(compiler, CODE_POP);
emitOp(compiler, CODE_POP);
}
// Starts a new local block scope.
@ -1231,15 +1262,18 @@ static int discardLocals(Compiler* compiler, int depth)
while (local >= 0 && compiler->locals[local].depth >= depth)
{
// If the local was closed over, make sure the upvalue gets closed when it
// goes out of scope on the stack.
// goes out of scope on the stack. We use emitByte() and not emitOp() here
// because we don't want to track that stack effect of these pops since the
// variables are still in scope after the break.
if (compiler->locals[local].isUpvalue)
{
emit(compiler, CODE_CLOSE_UPVALUE);
emitByte(compiler, CODE_CLOSE_UPVALUE);
}
else
{
emit(compiler, CODE_POP);
emitByte(compiler, CODE_POP);
}
local--;
}
@ -1252,7 +1286,9 @@ static int discardLocals(Compiler* compiler, int depth)
// temporaries are still on the stack.
static void popScope(Compiler* compiler)
{
compiler->numLocals -= discardLocals(compiler, compiler->scopeDepth);
int popped = discardLocals(compiler, compiler->scopeDepth);
compiler->numLocals -= popped;
compiler->numSlots -= popped;
compiler->scopeDepth--;
}
@ -1382,7 +1418,7 @@ static void loadLocal(Compiler* compiler, int slot)
{
if (slot <= 8)
{
emit(compiler, CODE_LOAD_LOCAL_0 + slot);
emitOp(compiler, (Code)(CODE_LOAD_LOCAL_0 + slot));
return;
}
@ -1416,7 +1452,7 @@ static ObjFn* endCompiler(Compiler* compiler,
// Mark the end of the bytecode. Since it may contain multiple early returns,
// we can't rely on CODE_RETURN to tell us we're at the end.
emit(compiler, CODE_END);
emitOp(compiler, CODE_END);
// Create a function object for the code we just compiled.
ObjFn* fn = wrenNewFunction(compiler->parser->vm,
@ -1424,6 +1460,7 @@ static ObjFn* endCompiler(Compiler* compiler,
compiler->constants.data,
compiler->constants.count,
compiler->numUpvalues,
compiler->maxSlots,
compiler->numParams,
compiler->bytecode.data,
compiler->bytecode.count,
@ -1454,8 +1491,8 @@ static ObjFn* endCompiler(Compiler* compiler,
// an upvalue.
for (int i = 0; i < compiler->numUpvalues; i++)
{
emit(compiler->parent, compiler->upvalues[i].isLocal ? 1 : 0);
emit(compiler->parent, compiler->upvalues[i].index);
emitByte(compiler->parent, compiler->upvalues[i].isLocal ? 1 : 0);
emitByte(compiler->parent, compiler->upvalues[i].index);
}
}
}
@ -1574,23 +1611,28 @@ static bool finishBlock(Compiler* compiler)
// In that case, this adds the code to ensure it returns `this`.
static void finishBody(Compiler* compiler, bool isInitializer)
{
// Now that the parameter list has been compiled, we know how many slots they
// use.
compiler->numSlots = compiler->numLocals;
compiler->maxSlots = compiler->numLocals;
bool isExpressionBody = finishBlock(compiler);
if (isInitializer)
{
// If the initializer body evaluates to a value, discard it.
if (isExpressionBody) emit(compiler, CODE_POP);
if (isExpressionBody) emitOp(compiler, CODE_POP);
// The receiver is always stored in the first local slot.
emit(compiler, CODE_LOAD_LOCAL_0);
emitOp(compiler, CODE_LOAD_LOCAL_0);
}
else if (!isExpressionBody)
{
// Implicitly return null in statement bodies.
emit(compiler, CODE_NULL);
emitOp(compiler, CODE_NULL);
}
emit(compiler, CODE_RETURN);
emitOp(compiler, CODE_RETURN);
}
// The VM can only handle a certain number of parameters, so check that we
@ -1971,8 +2013,8 @@ static void unaryOp(Compiler* compiler, bool allowAssignment)
static void boolean(Compiler* compiler, bool allowAssignment)
{
emit(compiler,
compiler->parser->previous.type == TOKEN_FALSE ? CODE_FALSE : CODE_TRUE);
emitOp(compiler,
compiler->parser->previous.type == TOKEN_FALSE ? CODE_FALSE : CODE_TRUE);
}
// Walks the compiler chain to find the compiler for the nearest class
@ -2119,7 +2161,7 @@ static void staticField(Compiler* compiler, bool allowAssignment)
int symbol = declareVariable(classCompiler, NULL);
// Implicitly initialize it to null.
emit(classCompiler, CODE_NULL);
emitOp(classCompiler, CODE_NULL);
defineVariable(classCompiler, symbol);
}
@ -2199,7 +2241,7 @@ static void name(Compiler* compiler, bool allowAssignment)
static void null(Compiler* compiler, bool allowAssignment)
{
emit(compiler, CODE_NULL);
emitOp(compiler, CODE_NULL);
}
// A number or string literal.
@ -2642,7 +2684,7 @@ void block(Compiler* compiler)
if (finishBlock(compiler))
{
// Block was an expression, so discard it.
emit(compiler, CODE_POP);
emitOp(compiler, CODE_POP);
}
popScope(compiler);
return;
@ -2993,14 +3035,14 @@ void statement(Compiler* compiler)
if (peek(compiler) == TOKEN_LINE)
{
// Implicitly return null if there is no value.
emit(compiler, CODE_NULL);
emitOp(compiler, CODE_NULL);
}
else
{
expression(compiler);
}
emit(compiler, CODE_RETURN);
emitOp(compiler, CODE_RETURN);
return;
}
@ -3011,7 +3053,7 @@ void statement(Compiler* compiler)
// Expression statement.
expression(compiler);
emit(compiler, CODE_POP);
emitOp(compiler, CODE_POP);
}
// Creates a matching constructor method for an initializer with [signature]
@ -3033,9 +3075,11 @@ static void createConstructor(Compiler* compiler, Signature* signature,
{
Compiler methodCompiler;
initCompiler(&methodCompiler, compiler->parser, compiler, false);
methodCompiler.numSlots = signature->arity + 1;
methodCompiler.maxSlots = methodCompiler.numSlots;
// Allocate the instance.
emit(&methodCompiler, compiler->enclosingClass->isForeign
emitOp(&methodCompiler, compiler->enclosingClass->isForeign
? CODE_FOREIGN_CONSTRUCT : CODE_CONSTRUCT);
// Run its initializer.
@ -3043,7 +3087,7 @@ static void createConstructor(Compiler* compiler, Signature* signature,
initializerSymbol);
// Return the instance.
emit(&methodCompiler, CODE_RETURN);
emitOp(&methodCompiler, CODE_RETURN);
endCompiler(&methodCompiler, "", 0);
}
@ -3178,7 +3222,7 @@ static void classDefinition(Compiler* compiler, bool isForeign)
int numFieldsInstruction = -1;
if (isForeign)
{
emit(compiler, CODE_FOREIGN_CLASS);
emitOp(compiler, CODE_FOREIGN_CLASS);
}
else
{
@ -3245,7 +3289,7 @@ static void import(Compiler* compiler)
emitShortArg(compiler, CODE_LOAD_MODULE, moduleConstant);
// Discard the unused result value from calling the module's fiber.
emit(compiler, CODE_POP);
emitOp(compiler, CODE_POP);
// The for clause is optional.
if (!match(compiler, TOKEN_FOR)) return;
@ -3368,8 +3412,8 @@ ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
}
}
emit(&compiler, CODE_NULL);
emit(&compiler, CODE_RETURN);
emitOp(&compiler, CODE_NULL);
emitOp(&compiler, CODE_RETURN);
// See if there are any implicitly declared module-level variables that never
// got an explicit definition.

View File

@ -349,7 +349,9 @@ int wrenDumpInstruction(WrenVM* vm, ObjFn* fn, int i)
void wrenDumpCode(WrenVM* vm, ObjFn* fn)
{
printf("%s: %s\n", fn->module->name->value, fn->debug->name);
printf("%s: %s\n",
fn->module->name == NULL ? "<core>" : fn->module->name->value,
fn->debug->name);
int i = 0;
int lastLine = -1;

View File

@ -1,6 +1,11 @@
// This defines the bytecode instructions used by the VM. It does so by invoking
// an OPCODE() macro which is expected to be defined at the point that this is
// included. See: http://en.wikipedia.org/wiki/X_Macro.
// included. (See: http://en.wikipedia.org/wiki/X_Macro for more.)
//
// The first argument is the name of the opcode. The second is its "stack
// effect" -- the amount that the op code changes the size of the stack. A
// stack effect of 1 means it pushes a value and the stack grows one larger.
// -2 means it pops two values, etc.
//
// Note that the order of instructions here affects the order of the dispatch
// table in the VM's interpreter loop. That in turn affects caching which
@ -8,137 +13,136 @@
// order here.
// Load the constant at index [arg].
OPCODE(CONSTANT)
OPCODE(CONSTANT, 1)
// Push null onto the stack.
OPCODE(NULL)
OPCODE(NULL, 1)
// Push false onto the stack.
OPCODE(FALSE)
OPCODE(FALSE, 1)
// Push true onto the stack.
OPCODE(TRUE)
OPCODE(TRUE, 1)
// Pushes the value in the given local slot.
OPCODE(LOAD_LOCAL_0)
OPCODE(LOAD_LOCAL_1)
OPCODE(LOAD_LOCAL_2)
OPCODE(LOAD_LOCAL_3)
OPCODE(LOAD_LOCAL_4)
OPCODE(LOAD_LOCAL_5)
OPCODE(LOAD_LOCAL_6)
OPCODE(LOAD_LOCAL_7)
OPCODE(LOAD_LOCAL_8)
OPCODE(LOAD_LOCAL_0, 1)
OPCODE(LOAD_LOCAL_1, 1)
OPCODE(LOAD_LOCAL_2, 1)
OPCODE(LOAD_LOCAL_3, 1)
OPCODE(LOAD_LOCAL_4, 1)
OPCODE(LOAD_LOCAL_5, 1)
OPCODE(LOAD_LOCAL_6, 1)
OPCODE(LOAD_LOCAL_7, 1)
OPCODE(LOAD_LOCAL_8, 1)
// Note: The compiler assumes the following _STORE instructions always
// immediately follow their corresponding _LOAD ones.
// Pushes the value in local slot [arg].
OPCODE(LOAD_LOCAL)
OPCODE(LOAD_LOCAL, 1)
// Stores the top of stack in local slot [arg]. Does not pop it.
OPCODE(STORE_LOCAL)
OPCODE(STORE_LOCAL, 0)
// Pushes the value in upvalue [arg].
OPCODE(LOAD_UPVALUE)
OPCODE(LOAD_UPVALUE, 1)
// Stores the top of stack in upvalue [arg]. Does not pop it.
OPCODE(STORE_UPVALUE)
OPCODE(STORE_UPVALUE, 0)
// Pushes the value of the top-level variable in slot [arg].
OPCODE(LOAD_MODULE_VAR)
OPCODE(LOAD_MODULE_VAR, 1)
// Stores the top of stack in top-level variable slot [arg]. Does not pop it.
OPCODE(STORE_MODULE_VAR)
OPCODE(STORE_MODULE_VAR, 0)
// Pushes the value of the field in slot [arg] of the receiver of the current
// function. This is used for regular field accesses on "this" directly in
// methods. This instruction is faster than the more general CODE_LOAD_FIELD
// instruction.
OPCODE(LOAD_FIELD_THIS)
OPCODE(LOAD_FIELD_THIS, 1)
// Stores the top of the stack in field slot [arg] in the receiver of the
// current value. Does not pop the value. This instruction is faster than the
// more general CODE_LOAD_FIELD instruction.
OPCODE(STORE_FIELD_THIS)
OPCODE(STORE_FIELD_THIS, 0)
// Pops an instance and pushes the value of the field in slot [arg] of it.
OPCODE(LOAD_FIELD)
OPCODE(LOAD_FIELD, 0)
// Pops an instance and stores the subsequent top of stack in field slot
// [arg] in it. Does not pop the value.
OPCODE(STORE_FIELD)
OPCODE(STORE_FIELD, -1)
// Pop and discard the top of stack.
OPCODE(POP)
OPCODE(POP, -1)
// Push a copy of the value currently on the top of the stack.
OPCODE(DUP)
OPCODE(DUP, 1)
// Invoke the method with symbol [arg]. The number indicates the number of
// arguments (not including the receiver).
OPCODE(CALL_0)
OPCODE(CALL_1)
OPCODE(CALL_2)
OPCODE(CALL_3)
OPCODE(CALL_4)
OPCODE(CALL_5)
OPCODE(CALL_6)
OPCODE(CALL_7)
OPCODE(CALL_8)
OPCODE(CALL_9)
OPCODE(CALL_10)
OPCODE(CALL_11)
OPCODE(CALL_12)
OPCODE(CALL_13)
OPCODE(CALL_14)
OPCODE(CALL_15)
OPCODE(CALL_16)
OPCODE(CALL_0, 0)
OPCODE(CALL_1, -1)
OPCODE(CALL_2, -2)
OPCODE(CALL_3, -3)
OPCODE(CALL_4, -4)
OPCODE(CALL_5, -5)
OPCODE(CALL_6, -6)
OPCODE(CALL_7, -7)
OPCODE(CALL_8, -8)
OPCODE(CALL_9, -9)
OPCODE(CALL_10, -10)
OPCODE(CALL_11, -11)
OPCODE(CALL_12, -12)
OPCODE(CALL_13, -13)
OPCODE(CALL_14, -14)
OPCODE(CALL_15, -15)
OPCODE(CALL_16, -16)
// Invoke a superclass method with symbol [arg]. The number indicates the
// number of arguments (not including the receiver).
OPCODE(SUPER_0)
OPCODE(SUPER_1)
OPCODE(SUPER_2)
OPCODE(SUPER_3)
OPCODE(SUPER_4)
OPCODE(SUPER_5)
OPCODE(SUPER_6)
OPCODE(SUPER_7)
OPCODE(SUPER_8)
OPCODE(SUPER_9)
OPCODE(SUPER_10)
OPCODE(SUPER_11)
OPCODE(SUPER_12)
OPCODE(SUPER_13)
OPCODE(SUPER_14)
OPCODE(SUPER_15)
OPCODE(SUPER_16)
OPCODE(SUPER_0, 0)
OPCODE(SUPER_1, -1)
OPCODE(SUPER_2, -2)
OPCODE(SUPER_3, -3)
OPCODE(SUPER_4, -4)
OPCODE(SUPER_5, -5)
OPCODE(SUPER_6, -6)
OPCODE(SUPER_7, -7)
OPCODE(SUPER_8, -8)
OPCODE(SUPER_9, -9)
OPCODE(SUPER_10, -10)
OPCODE(SUPER_11, -11)
OPCODE(SUPER_12, -12)
OPCODE(SUPER_13, -13)
OPCODE(SUPER_14, -14)
OPCODE(SUPER_15, -15)
OPCODE(SUPER_16, -16)
// Jump the instruction pointer [arg] forward.
OPCODE(JUMP)
OPCODE(JUMP, 0)
// Jump the instruction pointer [arg] backward. Pop and discard the top of
// the stack.
OPCODE(LOOP)
// Jump the instruction pointer [arg] backward.
OPCODE(LOOP, 0)
// Pop and if not truthy then jump the instruction pointer [arg] forward.
OPCODE(JUMP_IF)
OPCODE(JUMP_IF, -1)
// If the top of the stack is false, jump [arg] forward. Otherwise, pop and
// continue.
OPCODE(AND)
OPCODE(AND, -1)
// If the top of the stack is non-false, jump [arg] forward. Otherwise, pop
// and continue.
OPCODE(OR)
OPCODE(OR, -1)
// Close the upvalue for the local on the top of the stack, then pop it.
OPCODE(CLOSE_UPVALUE)
OPCODE(CLOSE_UPVALUE, -1)
// Exit from the current function and return the value on the top of the
// stack.
OPCODE(RETURN)
OPCODE(RETURN, 0)
// Creates a closure for the function stored at [arg] in the constant table.
//
@ -148,29 +152,29 @@ OPCODE(RETURN)
// upvalue being captured.
//
// Pushes the created closure.
OPCODE(CLOSURE)
OPCODE(CLOSURE, 1)
// Creates a new instance of a class.
//
// Assumes the class object is in slot zero, and replaces it with the new
// uninitialized instance of that class. This opcode is only emitted by the
// compiler-generated constructor metaclass methods.
OPCODE(CONSTRUCT)
OPCODE(CONSTRUCT, 0)
// Creates a new instance of a foreign class.
//
// Assumes the class object is in slot zero, and replaces it with the new
// uninitialized instance of that class. This opcode is only emitted by the
// compiler-generated constructor metaclass methods.
OPCODE(FOREIGN_CONSTRUCT)
OPCODE(FOREIGN_CONSTRUCT, 0)
// Creates a class. Top of stack is the superclass. Below that is a string for
// the name of the class. Byte [arg] is the number of fields in the class.
OPCODE(CLASS)
OPCODE(CLASS, -1)
// Creates a foreign class. Top of stack is the superclass. Below that is a
// string for the name of the class.
OPCODE(FOREIGN_CLASS)
OPCODE(FOREIGN_CLASS, -1)
// Define a method for symbol [arg]. The class receiving the method is popped
// off the stack, then the function defining the body is popped.
@ -178,7 +182,7 @@ OPCODE(FOREIGN_CLASS)
// If a foreign method is being defined, the "function" will be a string
// identifying the foreign method. Otherwise, it will be a function or
// closure.
OPCODE(METHOD_INSTANCE)
OPCODE(METHOD_INSTANCE, -2)
// Define a method for symbol [arg]. The class whose metaclass will receive
// the method is popped off the stack, then the function defining the body is
@ -187,20 +191,20 @@ OPCODE(METHOD_INSTANCE)
// If a foreign method is being defined, the "function" will be a string
// identifying the foreign method. Otherwise, it will be a function or
// closure.
OPCODE(METHOD_STATIC)
OPCODE(METHOD_STATIC, -2)
// Load the module whose name is stored in string constant [arg]. Pushes
// NULL onto the stack. If the module has already been loaded, does nothing
// else. Otherwise, it creates a fiber to run the desired module and switches
// to that. When that fiber is done, the current one is resumed.
OPCODE(LOAD_MODULE)
OPCODE(LOAD_MODULE, 1)
// Reads a top-level variable from another module. [arg1] is a string
// constant for the name of the module, and [arg2] is a string constant for
// the variable name. Pushes the variable if found, or generates a runtime
// error otherwise.
OPCODE(IMPORT_VARIABLE)
OPCODE(IMPORT_VARIABLE, 1)
// This pseudo-instruction indicates the end of the bytecode. It should
// always be preceded by a `CODE_RETURN`, so is never actually executed.
OPCODE(END)
OPCODE(END, 0)

View File

@ -186,7 +186,7 @@ ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size)
ObjFn* wrenNewFunction(WrenVM* vm, ObjModule* module,
const Value* constants, int numConstants,
int numUpvalues, int arity,
int numUpvalues, int maxSlots, int arity,
uint8_t* bytecode, int bytecodeLength,
const char* debugName, int debugNameLength,
int* sourceLines)
@ -222,6 +222,7 @@ ObjFn* wrenNewFunction(WrenVM* vm, ObjModule* module,
fn->bytecode = bytecode;
fn->constants = copiedConstants;
fn->module = module;
fn->maxSlots = maxSlots;
fn->numUpvalues = numUpvalues;
fn->numConstants = numConstants;
fn->arity = arity;

View File

@ -303,6 +303,9 @@ typedef struct
// The module where this function was defined.
ObjModule* module;
// The maximum number of stack slots this function may use.
int maxSlots;
int numUpvalues;
int numConstants;
@ -660,7 +663,7 @@ ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size);
// copy [constants] into its own array.
ObjFn* wrenNewFunction(WrenVM* vm, ObjModule* module,
const Value* constants, int numConstants,
int numUpvalues, int arity,
int numUpvalues, int maxSlots, int arity,
uint8_t* bytecode, int bytecodeLength,
const char* debugName, int debugNameLength,
int* sourceLines);

View File

@ -777,7 +777,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
#if WREN_COMPUTED_GOTO
static void* dispatchTable[] = {
#define OPCODE(name) &&code_##name,
#define OPCODE(name, _) &&code_##name,
#include "wren_opcodes.h"
#undef OPCODE
};
@ -1293,7 +1293,7 @@ static ObjFn* makeCallStub(WrenVM* vm, ObjModule* module, const char* signature)
int* debugLines = ALLOCATE_ARRAY(vm, int, 5);
memset(debugLines, 1, 5);
return wrenNewFunction(vm, module, NULL, 0, 0, 0, bytecode, 5,
return wrenNewFunction(vm, module, NULL, 0, 0, numParams + 1, 0, bytecode, 5,
signature, signatureLength, debugLines);
}

View File

@ -12,7 +12,7 @@
typedef enum
{
#define OPCODE(name) CODE_##name,
#define OPCODE(name, _) CODE_##name,
#include "wren_opcodes.h"
#undef OPCODE
} Code;