mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 22:28:45 +01:00
Get rid of the special "call" primitive result type.
Instead, Fn.call(...) is a special *method* type that has the same special sauce. The goal is eventually to get rid of the primitive result type entirely.
This commit is contained in:
@ -68,7 +68,7 @@ DEF_PRIMITIVE(fiber_new)
|
||||
DEF_PRIMITIVE(fiber_abort)
|
||||
{
|
||||
vm->fiber->error = args[1];
|
||||
|
||||
|
||||
// If the error is explicitly null, it's not really an abort.
|
||||
return IS_NULL(args[1]) ? PRIM_VALUE : PRIM_FIBER;
|
||||
}
|
||||
@ -117,7 +117,7 @@ static PrimitiveResult runFiber(WrenVM* vm, ObjFiber* fiber, Value* args,
|
||||
}
|
||||
|
||||
vm->fiber = fiber;
|
||||
|
||||
|
||||
return PRIM_FIBER;
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ DEF_PRIMITIVE(fiber_call1)
|
||||
|
||||
DEF_PRIMITIVE(fiber_current)
|
||||
{
|
||||
RETURN_OBJ(vm->fiber);
|
||||
RETURN_OBJ(vm->fiber);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fiber_error)
|
||||
@ -168,22 +168,22 @@ DEF_PRIMITIVE(fiber_transferError)
|
||||
{
|
||||
PrimitiveResult result = runFiber(vm, AS_FIBER(args[0]), args, false, true);
|
||||
if (result == PRIM_FIBER) vm->fiber->error = args[1];
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fiber_try)
|
||||
{
|
||||
ObjFiber* current = vm->fiber;
|
||||
ObjFiber* current = vm->fiber;
|
||||
ObjFiber* tried = AS_FIBER(args[0]);
|
||||
// TODO: Use runFiber().
|
||||
if (tried->numFrames == 0) RETURN_ERROR("Cannot try a finished fiber.");
|
||||
if (tried->caller != NULL) RETURN_ERROR("Fiber has already been called.");
|
||||
|
||||
vm->fiber = tried;
|
||||
|
||||
|
||||
// Remember who ran it.
|
||||
vm->fiber->caller = current;
|
||||
vm->fiber->caller = current;
|
||||
vm->fiber->callerIsTrying = true;
|
||||
|
||||
// If the fiber was yielded, make the yield call return null.
|
||||
@ -191,36 +191,36 @@ DEF_PRIMITIVE(fiber_try)
|
||||
{
|
||||
vm->fiber->stackTop[-1] = NULL_VAL;
|
||||
}
|
||||
|
||||
|
||||
return PRIM_FIBER;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fiber_yield)
|
||||
{
|
||||
ObjFiber* current = vm->fiber;
|
||||
vm->fiber = current->caller;
|
||||
|
||||
ObjFiber* current = vm->fiber;
|
||||
vm->fiber = current->caller;
|
||||
|
||||
// Unhook this fiber from the one that called it.
|
||||
current->caller = NULL;
|
||||
current->callerIsTrying = false;
|
||||
current->caller = NULL;
|
||||
current->callerIsTrying = false;
|
||||
|
||||
if (vm->fiber != NULL)
|
||||
{
|
||||
// Make the caller's run method return null.
|
||||
vm->fiber->stackTop[-1] = NULL_VAL;
|
||||
}
|
||||
|
||||
|
||||
return PRIM_FIBER;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fiber_yield1)
|
||||
{
|
||||
ObjFiber* current = vm->fiber;
|
||||
vm->fiber = current->caller;
|
||||
|
||||
ObjFiber* current = vm->fiber;
|
||||
vm->fiber = current->caller;
|
||||
|
||||
// Unhook this fiber from the one that called it.
|
||||
current->caller = NULL;
|
||||
current->callerIsTrying = false;
|
||||
current->caller = NULL;
|
||||
current->callerIsTrying = false;
|
||||
|
||||
if (vm->fiber != NULL)
|
||||
{
|
||||
@ -231,7 +231,7 @@ DEF_PRIMITIVE(fiber_yield1)
|
||||
// call in its stack. Since Fiber.yield(value) has two arguments (the Fiber
|
||||
// class and the value) and we only need one slot for the result, discard
|
||||
// the other slot now.
|
||||
current->stackTop--;
|
||||
current->stackTop--;
|
||||
}
|
||||
|
||||
return PRIM_FIBER;
|
||||
@ -250,41 +250,6 @@ DEF_PRIMITIVE(fn_arity)
|
||||
RETURN_NUM(AS_FN(args[0])->arity);
|
||||
}
|
||||
|
||||
static PrimitiveResult callFn(WrenVM* vm, Value* args, int numArgs)
|
||||
{
|
||||
ObjFn* fn;
|
||||
if (IS_CLOSURE(args[0]))
|
||||
{
|
||||
fn = AS_CLOSURE(args[0])->fn;
|
||||
}
|
||||
else
|
||||
{
|
||||
fn = AS_FN(args[0]);
|
||||
}
|
||||
|
||||
if (numArgs < fn->arity) RETURN_ERROR("Function expects more arguments.");
|
||||
|
||||
return PRIM_CALL;
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(fn_call0) { return callFn(vm, args, 0); }
|
||||
DEF_PRIMITIVE(fn_call1) { return callFn(vm, args, 1); }
|
||||
DEF_PRIMITIVE(fn_call2) { return callFn(vm, args, 2); }
|
||||
DEF_PRIMITIVE(fn_call3) { return callFn(vm, args, 3); }
|
||||
DEF_PRIMITIVE(fn_call4) { return callFn(vm, args, 4); }
|
||||
DEF_PRIMITIVE(fn_call5) { return callFn(vm, args, 5); }
|
||||
DEF_PRIMITIVE(fn_call6) { return callFn(vm, args, 6); }
|
||||
DEF_PRIMITIVE(fn_call7) { return callFn(vm, args, 7); }
|
||||
DEF_PRIMITIVE(fn_call8) { return callFn(vm, args, 8); }
|
||||
DEF_PRIMITIVE(fn_call9) { return callFn(vm, args, 9); }
|
||||
DEF_PRIMITIVE(fn_call10) { return callFn(vm, args, 10); }
|
||||
DEF_PRIMITIVE(fn_call11) { return callFn(vm, args, 11); }
|
||||
DEF_PRIMITIVE(fn_call12) { return callFn(vm, args, 12); }
|
||||
DEF_PRIMITIVE(fn_call13) { return callFn(vm, args, 13); }
|
||||
DEF_PRIMITIVE(fn_call14) { return callFn(vm, args, 14); }
|
||||
DEF_PRIMITIVE(fn_call15) { return callFn(vm, args, 15); }
|
||||
DEF_PRIMITIVE(fn_call16) { return callFn(vm, args, 16); }
|
||||
|
||||
DEF_PRIMITIVE(fn_toString)
|
||||
{
|
||||
RETURN_VAL(CONST_STRING(vm, "<fn>"));
|
||||
@ -1069,6 +1034,19 @@ 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 = wrenGetCoreModule(vm);
|
||||
@ -1153,23 +1131,23 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->fnClass->obj.classObj, "new(_)", fn_new);
|
||||
|
||||
PRIMITIVE(vm->fnClass, "arity", fn_arity);
|
||||
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);
|
||||
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, "toString", fn_toString);
|
||||
|
||||
vm->nullClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Null"));
|
||||
@ -1274,7 +1252,7 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
ObjClass* systemClass = AS_CLASS(wrenFindVariable(vm, coreModule, "System"));
|
||||
PRIMITIVE(systemClass->obj.classObj, "clock", system_clock);
|
||||
PRIMITIVE(systemClass->obj.classObj, "writeString_(_)", system_writeString);
|
||||
|
||||
|
||||
// While bootstrapping the core types and running the core library, a number
|
||||
// of string objects have been created, many of which were instantiated
|
||||
// before stringClass was stored in the VM. Some of them *must* be created
|
||||
|
||||
@ -250,17 +250,14 @@ typedef enum
|
||||
{
|
||||
// A normal value has been returned.
|
||||
PRIM_VALUE,
|
||||
|
||||
// A new callframe has been pushed.
|
||||
PRIM_CALL,
|
||||
|
||||
|
||||
// A fiber is being switched to. Also used for runtime errors (which also
|
||||
// change which fiber is being executed).
|
||||
PRIM_FIBER
|
||||
|
||||
} PrimitiveResult;
|
||||
|
||||
typedef PrimitiveResult (*Primitive)(WrenVM* vm, Value* args);
|
||||
typedef PrimitiveResult (*Primitive)(WrenVM* vm, Value* args);
|
||||
|
||||
// TODO: See if it's actually a perf improvement to have this in a separate
|
||||
// struct instead of in ObjFn.
|
||||
@ -352,6 +349,9 @@ 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
|
||||
@ -367,7 +367,7 @@ typedef struct
|
||||
{
|
||||
Primitive primitive;
|
||||
WrenForeignMethodFn foreign;
|
||||
|
||||
|
||||
// May be a [ObjFn] or [ObjClosure].
|
||||
Obj* obj;
|
||||
} fn;
|
||||
|
||||
@ -387,6 +387,33 @@ static void methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol)
|
||||
OBJ_VAL(classObj->name), vm->methodNames.data[symbol].buffer);
|
||||
}
|
||||
|
||||
// Checks that [value], which must be a function or 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)
|
||||
{
|
||||
ObjFn* fn;
|
||||
if (IS_CLOSURE(value))
|
||||
{
|
||||
fn = AS_CLOSURE(value)->fn;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(IS_FN(value), "Receiver must be a function or closure.");
|
||||
fn = AS_FN(value);
|
||||
}
|
||||
|
||||
// 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 [function] onto [fiber]'s callstack and invokes it. Expects [numArgs]
|
||||
// arguments (including the receiver) to be on the top of the stack already.
|
||||
// [function] can be an `ObjFn` or `ObjClosure`.
|
||||
@ -897,7 +924,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
case METHOD_PRIMITIVE:
|
||||
{
|
||||
// After calling this, the result will be in the first arg slot.
|
||||
switch (method->fn.primitive(vm, args))
|
||||
switch (method->fn.primitive(vm, args))
|
||||
{
|
||||
case PRIM_VALUE:
|
||||
// The result is now in the first arg slot. Discard the other
|
||||
@ -905,12 +932,6 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
fiber->stackTop -= numArgs - 1;
|
||||
break;
|
||||
|
||||
case PRIM_CALL:
|
||||
STORE_FRAME();
|
||||
callFunction(vm, fiber, AS_OBJ(args[0]), numArgs);
|
||||
LOAD_FRAME();
|
||||
break;
|
||||
|
||||
case PRIM_FIBER:
|
||||
STORE_FRAME();
|
||||
|
||||
@ -929,6 +950,14 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
case METHOD_FOREIGN:
|
||||
callForeign(vm, fiber, method->fn.foreign, numArgs);
|
||||
break;
|
||||
|
||||
case METHOD_FN_CALL:
|
||||
if (!checkArity(vm, args[0], numArgs)) RUNTIME_ERROR();
|
||||
|
||||
STORE_FRAME();
|
||||
callFunction(vm, fiber, AS_OBJ(args[0]), numArgs);
|
||||
LOAD_FRAME();
|
||||
break;
|
||||
|
||||
case METHOD_BLOCK:
|
||||
STORE_FRAME();
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
var f2 = Fn.new {|a, b| System.print(a, b) }
|
||||
var f2 = Fn.new {|a, b| System.print(a + b) }
|
||||
f2.call("a") // expect runtime error: Function expects more arguments.
|
||||
|
||||
Reference in New Issue
Block a user