mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
METHOD_CALL
opcode implemented
As of this commit there is only one method in the entier pocketlang thats List.append() have added (the reset is todo). method searching algorithm should be optimized in the future by sorting the methods according to their names and do a binary search
This commit is contained in:
parent
167355be59
commit
03bac026ee
@ -1926,7 +1926,11 @@ static void exprMap(Compiler* compiler) {
|
|||||||
consume(compiler, TK_RBRACE, "Expected '}' after map elements.");
|
consume(compiler, TK_RBRACE, "Expected '}' after map elements.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exprCall(Compiler* compiler) {
|
// This function is reused between calls and method calls. if the [call_type]
|
||||||
|
// is OP_METHOD_CALL the [method] should refer a string in the module's
|
||||||
|
// constant pool, otherwise it's ignored.
|
||||||
|
static void _compileCall(Compiler* compiler, Opcode call_type, int method) {
|
||||||
|
ASSERT((call_type == OP_CALL) || (call_type == OP_METHOD_CALL), OOPS);
|
||||||
|
|
||||||
// Compile parameters.
|
// Compile parameters.
|
||||||
int argc = 0;
|
int argc = 0;
|
||||||
@ -1940,14 +1944,24 @@ static void exprCall(Compiler* compiler) {
|
|||||||
consume(compiler, TK_RPARAN, "Expected ')' after parameter list.");
|
consume(compiler, TK_RPARAN, "Expected ')' after parameter list.");
|
||||||
}
|
}
|
||||||
|
|
||||||
emitOpcode(compiler, OP_CALL);
|
emitOpcode(compiler, call_type);
|
||||||
|
|
||||||
emitByte(compiler, argc);
|
emitByte(compiler, argc);
|
||||||
|
|
||||||
|
if (call_type == OP_METHOD_CALL) {
|
||||||
|
ASSERT_INDEX(method, (int)compiler->module->constants.count);
|
||||||
|
emitShort(compiler, method);
|
||||||
|
}
|
||||||
|
|
||||||
// After the call the arguments will be popped and the callable
|
// After the call the arguments will be popped and the callable
|
||||||
// will be replaced with the return value.
|
// will be replaced with the return value.
|
||||||
compilerChangeStack(compiler, -argc);
|
compilerChangeStack(compiler, -argc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void exprCall(Compiler* compiler) {
|
||||||
|
_compileCall(compiler, OP_CALL, -1);
|
||||||
|
}
|
||||||
|
|
||||||
static void exprAttrib(Compiler* compiler) {
|
static void exprAttrib(Compiler* compiler) {
|
||||||
consume(compiler, TK_NAME, "Expected an attribute name after '.'.");
|
consume(compiler, TK_NAME, "Expected an attribute name after '.'.");
|
||||||
const char* name = compiler->parser.previous.start;
|
const char* name = compiler->parser.previous.start;
|
||||||
@ -1958,6 +1972,12 @@ static void exprAttrib(Compiler* compiler) {
|
|||||||
moduleAddString(compiler->module, compiler->parser.vm,
|
moduleAddString(compiler->module, compiler->parser.vm,
|
||||||
name, length, &index);
|
name, length, &index);
|
||||||
|
|
||||||
|
// Check if it's a method call.
|
||||||
|
if (match(compiler, TK_LPARAN)) {
|
||||||
|
_compileCall(compiler, OP_METHOD_CALL, index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (compiler->l_value && matchAssignment(compiler)) {
|
if (compiler->l_value && matchAssignment(compiler)) {
|
||||||
TokenType assignment = compiler->parser.previous.type;
|
TokenType assignment = compiler->parser.previous.type;
|
||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
@ -3168,7 +3188,7 @@ PkResult compile(PKVM* vm, Module* module, const char* source,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if DUMP_BYTECODE
|
#if DUMP_BYTECODE
|
||||||
dumpFunctionCode(compiler->parser.vm, module->body);
|
dumpFunctionCode(compiler->parser.vm, module->body->fn);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Return the compilation result.
|
// Return the compilation result.
|
||||||
|
@ -115,9 +115,8 @@ void pkClassAddMethod(PKVM* vm, PkHandle* cls,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* pkGetSelf(const PKVM* vm) {
|
void* pkGetSelf(const PKVM* vm) {
|
||||||
Var self = vm->fiber->frames[vm->fiber->frame_count - 1].self;
|
ASSERT(IS_OBJ_TYPE(vm->fiber->self, OBJ_INST), OOPS);
|
||||||
ASSERT(IS_OBJ_TYPE(self, OBJ_INST), OOPS);
|
Instance* inst = (Instance*)AS_OBJ(vm->fiber->self);
|
||||||
Instance* inst = (Instance*)AS_OBJ(self);
|
|
||||||
ASSERT(inst->native != NULL, OOPS);
|
ASSERT(inst->native != NULL, OOPS);
|
||||||
return inst->native;
|
return inst->native;
|
||||||
}
|
}
|
||||||
@ -1243,11 +1242,15 @@ static void initializeCoreModules(PKVM* vm) {
|
|||||||
#undef DEF
|
#undef DEF
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* BUILTIN CLASS METHODS */
|
/* BUILTIN CLASS CONSTRUCTORS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static forceinline void _setSelf(PKVM* vm, Var value) {
|
static inline void _setSelf(PKVM* vm, Var value) {
|
||||||
vm->fiber->frames[vm->fiber->frame_count - 1].self = value;
|
vm->fiber->self = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Var _getSelf(PKVM* vm) {
|
||||||
|
return vm->fiber->self;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _ctorNull(PKVM* vm) {
|
static void _ctorNull(PKVM* vm) {
|
||||||
@ -1301,6 +1304,17 @@ static void _ctorFiber(PKVM* vm) {
|
|||||||
_setSelf(vm, VAR_OBJ(newFiber(vm, closure)));
|
_setSelf(vm, VAR_OBJ(newFiber(vm, closure)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* BUILTIN CLASS METHODS */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void _listAppend(PKVM* vm) {
|
||||||
|
Var self = _getSelf(vm);
|
||||||
|
ASSERT(IS_OBJ_TYPE(self, OBJ_LIST), OOPS);
|
||||||
|
listAppend(vm, ((List*)AS_OBJ(self)), ARG(1));
|
||||||
|
RET(self);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* BUILTIN CLASS INITIALIZATION */
|
/* BUILTIN CLASS INITIALIZATION */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -1333,7 +1347,23 @@ static void initializePrimitiveClasses(PKVM* vm) {
|
|||||||
ADD_CTOR(PK_MAP, "@ctorMap", _ctorMap, 0);
|
ADD_CTOR(PK_MAP, "@ctorMap", _ctorMap, 0);
|
||||||
ADD_CTOR(PK_FIBER, "@ctorFiber", _ctorFiber, 1);
|
ADD_CTOR(PK_FIBER, "@ctorFiber", _ctorFiber, 1);
|
||||||
|
|
||||||
// TODO: add methods.
|
#undef ADD_CTOR
|
||||||
|
|
||||||
|
#define ADD_METHOD(type, name, ptr, arity_) \
|
||||||
|
do { \
|
||||||
|
Function* fn = newFunction(vm, name, (int)strlen(name), \
|
||||||
|
NULL, true, NULL, NULL); \
|
||||||
|
fn->native = ptr; \
|
||||||
|
fn->arity = arity_; \
|
||||||
|
vmPushTempRef(vm, &fn->_super); /* fn. */ \
|
||||||
|
pkClosureBufferWrite(&vm->builtin_classes[type]->methods, \
|
||||||
|
vm, newClosure(vm, fn)); \
|
||||||
|
vmPopTempRef(vm); /* fn. */ \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
ADD_METHOD(PK_LIST, "append", _listAppend, 1);
|
||||||
|
|
||||||
|
#undef ADD_METHOD
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +282,24 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OP_METHOD_CALL:
|
||||||
|
{
|
||||||
|
int argc = READ_BYTE();
|
||||||
|
int index = READ_SHORT();
|
||||||
|
String* name = moduleGetStringAt(func->owner, index);
|
||||||
|
ASSERT(name != NULL, OOPS);
|
||||||
|
|
||||||
|
// Prints: %5d (argc) %d '%s'\n
|
||||||
|
PRINT_INT(argc);
|
||||||
|
PRINT(" (argc) ");
|
||||||
|
|
||||||
|
_PRINT_INT(index, 0);
|
||||||
|
PRINT(" '");
|
||||||
|
PRINT(name->data);
|
||||||
|
PRINT("'\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case OP_CALL:
|
case OP_CALL:
|
||||||
// Prints: %5d (argc)\n
|
// Prints: %5d (argc)\n
|
||||||
PRINT_INT(READ_BYTE());
|
PRINT_INT(READ_BYTE());
|
||||||
|
@ -123,6 +123,11 @@ OPCODE(POP, 0, -1)
|
|||||||
// params: 2 byte name index.
|
// params: 2 byte name index.
|
||||||
OPCODE(IMPORT, 2, 1)
|
OPCODE(IMPORT, 2, 1)
|
||||||
|
|
||||||
|
// Call a method on the variable at the stack top. See opcode CALL for detail.
|
||||||
|
// params: 2 bytes method name index in the constant pool.
|
||||||
|
// 1 byte argc.
|
||||||
|
OPCODE(METHOD_CALL, 3, -0) //< Stack size will be calculated at compile time.
|
||||||
|
|
||||||
// Calls a function using stack's top N values as the arguments and once it
|
// Calls a function using stack's top N values as the arguments and once it
|
||||||
// done the stack top should be stored otherwise it'll be disregarded. The
|
// done the stack top should be stored otherwise it'll be disregarded. The
|
||||||
// function should set the 0 th argment to return value.
|
// function should set the 0 th argment to return value.
|
||||||
|
@ -495,6 +495,7 @@ Fiber* newFiber(PKVM* vm, Closure* closure) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fiber->open_upvalues = NULL;
|
fiber->open_upvalues = NULL;
|
||||||
|
fiber->self = VAR_UNDEFINED;
|
||||||
|
|
||||||
// Initialize the return value to null (doesn't really have to do that here
|
// Initialize the return value to null (doesn't really have to do that here
|
||||||
// but if we're trying to debut it may crash when dumping the return value).
|
// but if we're trying to debut it may crash when dumping the return value).
|
||||||
|
@ -466,6 +466,13 @@ struct Fiber {
|
|||||||
// overflowed.
|
// overflowed.
|
||||||
Var* ret;
|
Var* ret;
|
||||||
|
|
||||||
|
// The self pointer to of the current method. It'll be updated before
|
||||||
|
// calling a native method. (Because native methods doesn't have a call
|
||||||
|
// frame we're doing it this way). Also updated just before calling a
|
||||||
|
// script method, and will be captured by the next allocated callframe
|
||||||
|
// and reset to VAR_UNDEFINED.
|
||||||
|
Var self;
|
||||||
|
|
||||||
// Heap allocated array of call frames will grow as needed.
|
// Heap allocated array of call frames will grow as needed.
|
||||||
CallFrame* frames;
|
CallFrame* frames;
|
||||||
int frame_capacity; //< Capacity of the frames array.
|
int frame_capacity; //< Capacity of the frames array.
|
||||||
|
65
src/pk_vm.c
65
src/pk_vm.c
@ -550,7 +550,10 @@ static inline void pushCallFrame(PKVM* vm, const Closure* closure, Var* rbp) {
|
|||||||
frame->rbp = rbp;
|
frame->rbp = rbp;
|
||||||
frame->closure = closure;
|
frame->closure = closure;
|
||||||
frame->ip = closure->fn->fn->opcodes.data;
|
frame->ip = closure->fn->fn->opcodes.data;
|
||||||
frame->self = VAR_UNDEFINED;
|
|
||||||
|
// Eat the self.
|
||||||
|
frame->self = vm->fiber->self;
|
||||||
|
vm->fiber->self = VAR_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void reuseCallFrame(PKVM* vm, const Closure* closure) {
|
static inline void reuseCallFrame(PKVM* vm, const Closure* closure) {
|
||||||
@ -564,6 +567,7 @@ static inline void reuseCallFrame(PKVM* vm, const Closure* closure) {
|
|||||||
CallFrame* frame = fb->frames + fb->frame_count - 1;
|
CallFrame* frame = fb->frames + fb->frame_count - 1;
|
||||||
frame->closure = closure;
|
frame->closure = closure;
|
||||||
frame->ip = closure->fn->fn->opcodes.data;
|
frame->ip = closure->fn->fn->opcodes.data;
|
||||||
|
frame->self = VAR_UNDEFINED;
|
||||||
|
|
||||||
ASSERT(*frame->rbp == VAR_NULL, OOPS);
|
ASSERT(*frame->rbp == VAR_NULL, OOPS);
|
||||||
|
|
||||||
@ -791,12 +795,18 @@ L_vm_main_loop:
|
|||||||
// defined, the next line become a declaration (Opcode instruction;).
|
// defined, the next line become a declaration (Opcode instruction;).
|
||||||
NO_OP;
|
NO_OP;
|
||||||
|
|
||||||
|
#define _DUMP_STACK() \
|
||||||
|
do { \
|
||||||
|
system("cls"); /* FIXME: */ \
|
||||||
|
dumpGlobalValues(vm); \
|
||||||
|
dumpStackFrame(vm); \
|
||||||
|
DEBUG_BREAK(); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
#if DUMP_STACK
|
#if DUMP_STACK
|
||||||
system("cls"); // FIXME:
|
_DUMP_STACK();
|
||||||
dumpGlobalValues(vm);
|
|
||||||
dumpStackFrame(vm);
|
|
||||||
DEBUG_BREAK();
|
|
||||||
#endif
|
#endif
|
||||||
|
#undef _DUMP_STACK
|
||||||
|
|
||||||
SWITCH() {
|
SWITCH() {
|
||||||
|
|
||||||
@ -1045,32 +1055,49 @@ L_vm_main_loop:
|
|||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t argc;
|
||||||
|
Var callable;
|
||||||
|
const Closure* closure;
|
||||||
|
|
||||||
|
OPCODE(METHOD_CALL):
|
||||||
|
argc = READ_BYTE();
|
||||||
|
fiber->ret = (fiber->sp - argc - 1);
|
||||||
|
fiber->self = *fiber->ret; //< Self for the next call.
|
||||||
|
|
||||||
|
uint16_t index = READ_SHORT();
|
||||||
|
bool is_method;
|
||||||
|
String* name = moduleGetStringAt(module, (int)index);
|
||||||
|
callable = getMethod(vm, fiber->self, name, &is_method);
|
||||||
|
CHECK_ERROR();
|
||||||
|
goto L_do_call;
|
||||||
|
|
||||||
OPCODE(CALL):
|
OPCODE(CALL):
|
||||||
OPCODE(TAIL_CALL):
|
OPCODE(TAIL_CALL):
|
||||||
{
|
argc = READ_BYTE();
|
||||||
const uint8_t argc = READ_BYTE();
|
fiber->ret = fiber->sp - argc - 1;
|
||||||
Var* callable = fiber->sp - argc - 1;
|
callable = *fiber->ret;
|
||||||
|
|
||||||
const Closure* closure = NULL;
|
|
||||||
|
|
||||||
|
L_do_call:
|
||||||
// Raw functions cannot be on the stack, since they're not first class
|
// Raw functions cannot be on the stack, since they're not first class
|
||||||
// citizens.
|
// citizens.
|
||||||
ASSERT(!IS_OBJ_TYPE(*callable, OBJ_FUNC), OOPS);
|
ASSERT(!IS_OBJ_TYPE(callable, OBJ_FUNC), OOPS);
|
||||||
|
|
||||||
if (IS_OBJ_TYPE(*callable, OBJ_CLOSURE)) {
|
if (IS_OBJ_TYPE(callable, OBJ_CLOSURE)) {
|
||||||
closure = (const Closure*)AS_OBJ(*callable);
|
closure = (const Closure*)AS_OBJ(callable);
|
||||||
|
|
||||||
} else if (IS_OBJ_TYPE(*callable, OBJ_CLASS)) {
|
} else if (IS_OBJ_TYPE(callable, OBJ_CLASS)) {
|
||||||
closure = (const Closure*)((Class*)AS_OBJ(*callable))->ctor;
|
closure = (const Closure*)((Class*)AS_OBJ(callable))->ctor;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
RUNTIME_ERROR(stringFormat(vm, "$ $(@).", "Expected a callable to "
|
RUNTIME_ERROR(stringFormat(vm, "$ $(@).", "Expected a callable to "
|
||||||
"call, instead got",
|
"call, instead got",
|
||||||
varTypeName(*callable), toString(vm, *callable)));
|
varTypeName(callable), toString(vm, callable)));
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we reached here it's a valid callable.
|
// If we reached here it's a valid callable.
|
||||||
|
ASSERT(closure != NULL, OOPS);
|
||||||
|
|
||||||
// -1 argument means multiple number of args.
|
// -1 argument means multiple number of args.
|
||||||
if (closure->fn->arity != -1 && closure->fn->arity != argc) {
|
if (closure->fn->arity != -1 && closure->fn->arity != argc) {
|
||||||
@ -1080,8 +1107,6 @@ L_vm_main_loop:
|
|||||||
RUNTIME_ERROR(msg);
|
RUNTIME_ERROR(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next call frame starts here. (including return value).
|
|
||||||
fiber->ret = callable;
|
|
||||||
*(fiber->ret) = VAR_NULL; //< Set the return value to null.
|
*(fiber->ret) = VAR_NULL; //< Set the return value to null.
|
||||||
|
|
||||||
if (closure->fn->is_native) {
|
if (closure->fn->is_native) {
|
||||||
@ -1116,9 +1141,9 @@ L_vm_main_loop:
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (instruction == OP_CALL) {
|
if (instruction == OP_CALL || instruction == OP_METHOD_CALL) {
|
||||||
UPDATE_FRAME(); //< Update the current frame's ip.
|
UPDATE_FRAME(); //< Update the current frame's ip.
|
||||||
pushCallFrame(vm, closure, callable);
|
pushCallFrame(vm, closure, fiber->ret);
|
||||||
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
|
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user