1
0
forked from Mirror/wren

Remove the special method type for Fn.call(...).

Instead, using regular primitives for each of the call() methods.
Doesn't help performance (alas), but does seem cleaner to me.
This commit is contained in:
Bob Nystrom
2018-07-18 08:20:11 -07:00
parent ed64a13778
commit 5f29a72d65
7 changed files with 101 additions and 105 deletions

View File

@ -84,6 +84,10 @@ benchmark: release
$(V) $(MAKE) -f util/wren.mk api_test
$(V) ./util/benchmark.py -l wren $(suite)
benchmark_baseline: release
$(V) $(MAKE) -f util/wren.mk api_test
$(V) ./util/benchmark.py --generate-baseline
unit_test:
$(V) $(MAKE) -f util/wren.mk MODE=debug unit_test
$(V) ./build/debug/test/unit_wrend

View File

@ -240,6 +240,44 @@ DEF_PRIMITIVE(fn_arity)
RETURN_NUM(AS_CLOSURE(args[0])->fn->arity);
}
static void call(WrenVM* vm, Value* args, int numArgs)
{
// We only care about missing arguments, not extras.
if (AS_CLOSURE(args[0])->fn->arity > numArgs)
{
vm->fiber->error = CONST_STRING(vm, "Function expects more arguments.");
return;
}
// +1 to include the function itself.
wrenCallFunction(vm, vm->fiber, AS_CLOSURE(args[0]), numArgs + 1);
}
#define DEF_FN_CALL(numArgs) \
DEF_PRIMITIVE(fn_call##numArgs) \
{ \
call(vm, args, numArgs); \
return false; \
} \
DEF_FN_CALL(0)
DEF_FN_CALL(1)
DEF_FN_CALL(2)
DEF_FN_CALL(3)
DEF_FN_CALL(4)
DEF_FN_CALL(5)
DEF_FN_CALL(6)
DEF_FN_CALL(7)
DEF_FN_CALL(8)
DEF_FN_CALL(9)
DEF_FN_CALL(10)
DEF_FN_CALL(11)
DEF_FN_CALL(12)
DEF_FN_CALL(13)
DEF_FN_CALL(14)
DEF_FN_CALL(15)
DEF_FN_CALL(16)
DEF_PRIMITIVE(fn_toString)
{
RETURN_VAL(CONST_STRING(vm, "<fn>"));
@ -1097,19 +1135,6 @@ static ObjClass* defineClass(WrenVM* vm, ObjModule* module, const char* name)
return classObj;
}
// Defines one of the overloads of the special "call(...)" method on Fn.
//
// These methods have their own unique method type to handle pushing the
// function onto the callstack and checking its arity.
static void fnCall(WrenVM* vm, const char* signature)
{
int symbol = wrenSymbolTableEnsure(vm, &vm->methodNames, signature,
strlen(signature));
Method method;
method.type = METHOD_FN_CALL;
wrenBindMethod(vm, vm->fnClass, symbol, method);
}
void wrenInitializeCore(WrenVM* vm)
{
ObjModule* coreModule = wrenNewModule(vm, NULL);
@ -1199,23 +1224,23 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->fnClass->obj.classObj, "new(_)", fn_new);
PRIMITIVE(vm->fnClass, "arity", fn_arity);
fnCall(vm, "call()");
fnCall(vm, "call(_)");
fnCall(vm, "call(_,_)");
fnCall(vm, "call(_,_,_)");
fnCall(vm, "call(_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_,_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)");
fnCall(vm, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)");
PRIMITIVE(vm->fnClass, "call()", fn_call0);
PRIMITIVE(vm->fnClass, "call(_)", fn_call1);
PRIMITIVE(vm->fnClass, "call(_,_)", fn_call2);
PRIMITIVE(vm->fnClass, "call(_,_,_)", fn_call3);
PRIMITIVE(vm->fnClass, "call(_,_,_,_)", fn_call4);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_)", fn_call5);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_)", fn_call6);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_)", fn_call7);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_)", fn_call8);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_)", fn_call9);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_)", fn_call10);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_)", fn_call11);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_)", fn_call12);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call13);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call14);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call15);
PRIMITIVE(vm->fnClass, "call(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_)", fn_call16);
PRIMITIVE(vm->fnClass, "toString", fn_toString);
vm->nullClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Null"));

View File

@ -11,7 +11,7 @@
&vm->methodNames, name, strlen(name)); \
Method method; \
method.type = METHOD_PRIMITIVE; \
method.fn.primitive = prim_##function; \
method.as.primitive = prim_##function; \
wrenBindMethod(vm, cls, symbol, method); \
}

View File

@ -1008,7 +1008,7 @@ static void blackenClass(WrenVM* vm, ObjClass* classObj)
{
if (classObj->methods.data[i].type == METHOD_BLOCK)
{
wrenGrayObj(vm, (Obj*)classObj->methods.data[i].fn.obj);
wrenGrayObj(vm, (Obj*)classObj->methods.data[i].as.closure);
}
}

View File

@ -349,9 +349,6 @@ typedef enum
// A normal user-defined method.
METHOD_BLOCK,
// The special "call(...)" methods on function.
METHOD_FN_CALL,
// No method for the given symbol.
METHOD_NONE
} MethodType;
@ -366,10 +363,8 @@ typedef struct
{
Primitive primitive;
WrenForeignMethodFn foreign;
// May be a [ObjFn] or [ObjClosure].
ObjClosure* obj;
} fn;
ObjClosure* closure;
} as;
} Method;
DECLARE_BUFFER(Method, Method);

View File

@ -342,12 +342,12 @@ static void bindMethod(WrenVM* vm, int methodType, int symbol,
{
const char* name = AS_CSTRING(methodValue);
method.type = METHOD_FOREIGN;
method.fn.foreign = findForeignMethod(vm, module->name->value,
method.as.foreign = findForeignMethod(vm, module->name->value,
className,
methodType == CODE_METHOD_STATIC,
name);
if (method.fn.foreign == NULL)
if (method.as.foreign == NULL)
{
vm->fiber->error = wrenStringFormat(vm,
"Could not find foreign method '@' for class $ in module '$'.",
@ -357,11 +357,11 @@ static void bindMethod(WrenVM* vm, int methodType, int symbol,
}
else
{
method.fn.obj = AS_CLOSURE(methodValue);
method.as.closure = AS_CLOSURE(methodValue);
method.type = METHOD_BLOCK;
// Patch up the bytecode now that we know the superclass.
wrenBindMethodCode(classObj, method.fn.obj->fn);
wrenBindMethodCode(classObj, method.as.closure->fn);
}
wrenBindMethod(vm, classObj, symbol, method);
@ -427,48 +427,6 @@ static void methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol)
OBJ_VAL(classObj->name), vm->methodNames.data[symbol]->value);
}
// Checks that [value], which must be a closure, does not require more
// parameters than are provided by [numArgs].
//
// If there are not enough arguments, aborts the current fiber and returns
// `false`.
static bool checkArity(WrenVM* vm, Value value, int numArgs)
{
ASSERT(IS_CLOSURE(value), "Receiver must be a closure.");
ObjFn* fn = AS_CLOSURE(value)->fn;
// We only care about missing arguments, not extras. The "- 1" is because
// numArgs includes the receiver, the function itself, which we don't want to
// count.
if (numArgs - 1 >= fn->arity) return true;
vm->fiber->error = CONST_STRING(vm, "Function expects more arguments.");
return false;
}
// Pushes [closure] onto [fiber]'s callstack and invokes it. Expects [numArgs]
// arguments (including the receiver) to be on the top of the stack already.
static inline void callFunction(
WrenVM* vm, ObjFiber* fiber, ObjClosure* closure, int numArgs)
{
// Grow the call frame array if needed.
if (fiber->numFrames + 1 > fiber->frameCapacity)
{
int max = fiber->frameCapacity * 2;
fiber->frames = (CallFrame*)wrenReallocate(vm, fiber->frames,
sizeof(CallFrame) * fiber->frameCapacity,
sizeof(CallFrame) * max);
fiber->frameCapacity = max;
}
// Grow the stack if needed.
int stackSize = (int)(fiber->stackTop - fiber->stack);
int needed = stackSize + closure->fn->maxSlots;
wrenEnsureStack(vm, fiber, needed);
wrenAppendCallFrame(vm, fiber, closure, fiber->stackTop - numArgs);
}
// Looks up the previously loaded module with [name].
//
// Returns `NULL` if no module with that name has been loaded.
@ -611,7 +569,7 @@ static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module)
int symbol = wrenSymbolTableEnsure(vm, &vm->methodNames, "<allocate>", 10);
if (methods.allocate != NULL)
{
method.fn.foreign = methods.allocate;
method.as.foreign = methods.allocate;
wrenBindMethod(vm, classObj, symbol, method);
}
@ -620,7 +578,7 @@ static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module)
symbol = wrenSymbolTableEnsure(vm, &vm->methodNames, "<finalize>", 10);
if (methods.finalize != NULL)
{
method.fn.foreign = (WrenForeignMethodFn)methods.finalize;
method.as.foreign = (WrenForeignMethodFn)methods.finalize;
wrenBindMethod(vm, classObj, symbol, method);
}
}
@ -669,7 +627,7 @@ static void createForeign(WrenVM* vm, ObjFiber* fiber, Value* stack)
Value* oldApiStack = vm->apiStack;
vm->apiStack = stack;
method->fn.foreign(vm);
method->as.foreign(vm);
vm->apiStack = oldApiStack;
// TODO: Check that allocateForeign was called.
@ -693,7 +651,7 @@ void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign)
ASSERT(method->type == METHOD_FOREIGN, "Finalizer should be foreign.");
WrenFinalizerFn finalizer = (WrenFinalizerFn)method->fn.foreign;
WrenFinalizerFn finalizer = (WrenFinalizerFn)method->as.foreign;
finalizer(foreign->data);
}
@ -1033,13 +991,13 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
switch (method->type)
{
case METHOD_PRIMITIVE:
if (method->fn.primitive(vm, args))
if (method->as.primitive(vm, args))
{
// The result is now in the first arg slot. Discard the other
// stack slots.
fiber->stackTop -= numArgs - 1;
} else {
// An error or fiber switch occurred.
// An error, fiber switch, or call frame change occurred.
STORE_FRAME();
// If we don't have a fiber to switch to, stop interpreting.
@ -1051,21 +1009,13 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
break;
case METHOD_FOREIGN:
callForeign(vm, fiber, method->fn.foreign, numArgs);
callForeign(vm, fiber, method->as.foreign, numArgs);
if (!IS_NULL(fiber->error)) RUNTIME_ERROR();
break;
case METHOD_FN_CALL:
if (!checkArity(vm, args[0], numArgs)) RUNTIME_ERROR();
STORE_FRAME();
callFunction(vm, fiber, AS_CLOSURE(args[0]), numArgs);
LOAD_FRAME();
break;
case METHOD_BLOCK:
STORE_FRAME();
callFunction(vm, fiber, (ObjClosure*)method->fn.obj, numArgs);
wrenCallFunction(vm, fiber, (ObjClosure*)method->as.closure, numArgs);
LOAD_FRAME();
break;
@ -1327,7 +1277,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
{
STORE_FRAME();
ObjClosure* closure = AS_CLOSURE(PEEK());
callFunction(vm, fiber, closure, 1);
wrenCallFunction(vm, fiber, closure, 1);
LOAD_FRAME();
}
else
@ -1433,7 +1383,7 @@ WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method)
// function has exactly one slot for each argument.
vm->fiber->stackTop = &vm->fiber->stack[closure->fn->maxSlots];
callFunction(vm, vm->fiber, closure, 0);
wrenCallFunction(vm, vm->fiber, closure, 0);
return runInterpreter(vm, vm->fiber);
}

View File

@ -174,10 +174,32 @@ int wrenDeclareVariable(WrenVM* vm, ObjModule* module, const char* name,
int wrenDefineVariable(WrenVM* vm, ObjModule* module, const char* name,
size_t length, Value value);
// Mark [obj] as a GC root so that it doesn't get collected.
// Pushes [closure] onto [fiber]'s callstack to invoke it. Expects [numArgs]
// arguments (including the receiver) to be on the top of the stack already.
static inline void wrenCallFunction(WrenVM* vm, ObjFiber* fiber,
ObjClosure* closure, int numArgs)
{
// Grow the call frame array if needed.
if (fiber->numFrames + 1 > fiber->frameCapacity)
{
int max = fiber->frameCapacity * 2;
fiber->frames = (CallFrame*)wrenReallocate(vm, fiber->frames,
sizeof(CallFrame) * fiber->frameCapacity, sizeof(CallFrame) * max);
fiber->frameCapacity = max;
}
// Grow the stack if needed.
int stackSize = (int)(fiber->stackTop - fiber->stack);
int needed = stackSize + closure->fn->maxSlots;
wrenEnsureStack(vm, fiber, needed);
wrenAppendCallFrame(vm, fiber, closure, fiber->stackTop - numArgs);
}
// Marks [obj] as a GC root so that it doesn't get collected.
void wrenPushRoot(WrenVM* vm, Obj* obj);
// Remove the most recently pushed temporary root.
// Removes the most recently pushed temporary root.
void wrenPopRoot(WrenVM* vm);
// Returns the class of [value].