From 48089b2a1a3f89f35095d89c696ef13ef6934235 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Mon, 16 May 2022 17:28:10 +0530 Subject: [PATCH] native instance interface implemented --- cli/modules/std_dummy.c | 29 ++++++++++++ src/include/pocketlang.h | 34 +++++++++++++- src/pk_public.c | 96 +++++++++++++++++++++++++++++++++++++++- src/pk_value.c | 14 ++++++ src/pk_value.h | 2 + src/pk_vm.c | 11 +++-- tests/modules/dummy.pk | 8 +++- tests/native/example2.c | 62 +++++++++++++++++++------- 8 files changed, 233 insertions(+), 23 deletions(-) diff --git a/cli/modules/std_dummy.c b/cli/modules/std_dummy.c index bee72e9..9b3b6a3 100644 --- a/cli/modules/std_dummy.c +++ b/cli/modules/std_dummy.c @@ -24,6 +24,14 @@ void _deleteDummy(void* ptr) { FREE_OBJ(dummy); } +DEF(_dummyInit, "") { + double val; + if (!pkValidateSlotNumber(vm, 1, &val)) return; + + Dummy* self = (Dummy*) pkGetSelf(vm); + self->val = val; +} + DEF(_dummyGetter, "") { const char* name = pkGetSlotString(vm, 1, NULL); Dummy* self = (Dummy*)pkGetSelf(vm); @@ -44,6 +52,25 @@ DEF(_dummySetter, "") { } } +DEF(_dummyAdd, "") { + Dummy* self = (Dummy*) pkGetSelf(vm); + + pkReserveSlots(vm, 4); // Now we have slots [0, 1, 2, 3]. + + pkPlaceSelf(vm, 2); // slot[2] = self + pkGetClass(vm, 2, 2); // slot[2] = Dummy class. + + // slots[1] = other. + if (!pkValidateSlotInstanceOf(vm, 1, 2)) return; + Dummy* other = (Dummy*) pkGetSlotNativeInstance(vm, 1); + + // slot[3] = self.val + other.val + pkSetSlotNumber(vm, 3, self->val + other->val); + + // slot[0] = Dummy(slot[3]) => return value. + if (!pkNewInstance(vm, 2, 0, 1, 3)) return; +} + DEF(_dummyEq, "") { // TODO: Currently there is no way of getting another native instance @@ -85,8 +112,10 @@ void registerModuleDummy(PKVM* vm) { PkHandle* cls_dummy = pkNewClass(vm, "Dummy", NULL, dummy, _newDummy, _deleteDummy); + pkClassAddMethod(vm, cls_dummy, "_init", _dummyInit, 1); pkClassAddMethod(vm, cls_dummy, "@getter", _dummyGetter, 1); pkClassAddMethod(vm, cls_dummy, "@setter", _dummySetter, 2); + pkClassAddMethod(vm, cls_dummy, "+", _dummyAdd, 1); pkClassAddMethod(vm, cls_dummy, "==", _dummyEq, 1); pkClassAddMethod(vm, cls_dummy, ">", _dummyGt, 1); pkClassAddMethod(vm, cls_dummy, "a_method", _dummyMethod, 2); diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index 611e3df..dc76b26 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -283,8 +283,6 @@ PK_PUBLIC int pkGetArgc(const PKVM* vm); // that min <= max, and pocketlang won't validate this in release binary. PK_PUBLIC bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max); -// SLOTS DOCS - // Helper function to check if the argument at the [arg] slot is Boolean and // if not set a runtime error. PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value); @@ -298,6 +296,16 @@ PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value); PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, const char** value, uint32_t* length); +// Helper function to check if the argument at the [arg] slot is an instance +// of the class which is at the [cls] index. If not set a runtime error. +PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int arg, int cls); + +// Helper function to check if the instance at the [inst] slot is an instance +// of the class which is at the [cls] index. The value will be set to [val] +// if the object at [cls] slot isn't a valid class a runtime error will be set +// and return false. +PK_PUBLIC bool pkIsSlotInstanceOf(PKVM* vm, int inst, int cls, bool* val); + // Make sure the fiber has [count] number of slots to work with (including the // arguments). PK_PUBLIC void pkReserveSlots(PKVM* vm, int count); @@ -328,6 +336,10 @@ PK_PUBLIC const char* pkGetSlotString(PKVM* vm, int index, uint32_t* length); // garbage collected. PK_PUBLIC PkHandle* pkGetSlotHandle(PKVM* vm, int index); +// Returns the native instance at the [index] slot. If the value at the [index] +// is not a valid native instance, an assertion will fail. +PK_PUBLIC void* pkGetSlotNativeInstance(PKVM* vm, int index); + // Set the [index] slot value as pocketlang null. PK_PUBLIC void pkSetSlotNull(PKVM* vm, int index); @@ -345,6 +357,10 @@ PK_PUBLIC void pkSetSlotString(PKVM* vm, int index, const char* value); PK_PUBLIC void pkSetSlotStringLength(PKVM* vm, int index, const char* value, uint32_t length); +// Create a new string copying from the formated string and set it to [index] +// slot. +PK_PUBLIC void pkSetSlotStringFmt(PKVM* vm, int index, const char* fmt, ...); + // Set the [index] slot's value as the given [handle]. The function won't // reclaim the ownership of the handle and you can still use it till // it's released by yourself. @@ -354,6 +370,20 @@ PK_PUBLIC void pkSetSlotHandle(PKVM* vm, int index, PkHandle* handle); // [global] slot with the given [name]. PK_PUBLIC void pkSetGlobal(PKVM* vm, int module, int global, const char* name); +// Creates a new instance of class at the [cls] slot, calls the constructor, +// and place it at the [index] slot. Returns true if the instance constructed +// successfully. +// +// [argc] is the argument count for the constructor, and [argv] +// is the first argument slot's index. +PK_PUBLIC bool pkNewInstance(PKVM* vm, int cls, int index, int argc, int argv); + +// Place the [self] instance at the [index] slot. +PK_PUBLIC void pkPlaceSelf(PKVM* vm, int index); + +// Set the [index] slot's value as the class of the [instance]. +PK_PUBLIC void pkGetClass(PKVM* vm, int instance, int index); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/pk_public.c b/src/pk_public.c index 2ccd7d5..0f37438 100644 --- a/src/pk_public.c +++ b/src/pk_public.c @@ -576,6 +576,32 @@ PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, const char** value, return true; } +PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int arg, int cls) { + CHECK_FIBER_EXISTS(vm); + VALIDATE_ARGC(arg); + VALIDATE_SLOT_INDEX(cls); + + Var instance = ARG(arg), class_ = SLOT(cls); + if (!varIsType(vm, instance, class_)) { + // If [class_] is not a valid class, it's already an error. + if (VM_HAS_ERROR(vm)) return false; + ERR_INVALID_ARG_TYPE(((Class*)AS_OBJ(class_))->name->data); + return false; + } + + return true; +} + +bool pkIsSlotInstanceOf(PKVM* vm, int inst, int cls, bool* val) { + CHECK_ARG_NULL(val); + VALIDATE_SLOT_INDEX(inst); + VALIDATE_SLOT_INDEX(cls); + + Var instance = SLOT(inst), class_ = SLOT(cls); + *val = varIsType(vm, inst, cls); + return !VM_HAS_ERROR(vm); +} + void pkReserveSlots(PKVM* vm, int count) { if (vm->fiber == NULL) vm->fiber = newFiber(vm, NULL); int needed = (int)(vm->fiber->ret - vm->fiber->stack) + count; @@ -584,7 +610,7 @@ void pkReserveSlots(PKVM* vm, int count) { int pkGetSlotsCount(PKVM* vm) { CHECK_FIBER_EXISTS(vm); - return (int) ((vm->fiber->stack + vm->fiber->stack_size) - vm->fiber->ret); + return vm->fiber->stack_size - (int)(vm->fiber->ret - vm->fiber->stack); } PkVarType pkGetSlotType(PKVM* vm, int index) { @@ -623,6 +649,21 @@ PkHandle* pkGetSlotHandle(PKVM* vm, int index) { return vmNewHandle(vm, SLOT(index)); } +void* pkGetSlotNativeInstance(PKVM* vm, int index) { + CHECK_FIBER_EXISTS(vm); + VALIDATE_SLOT_INDEX(index); + + Var value = SLOT(index); + ASSERT(IS_OBJ_TYPE(value, OBJ_INST), "Slot value wasn't an Instance"); + + // TODO: If the native initializer (pkNewInstanceFn()) returned NULL, + // [inst->native] will be null - handle. + Instance* inst = (Instance*) AS_OBJ(value); + ASSERT(inst->native != NULL, "Slot value wasn't a Native Instance"); + + return inst->native; +} + void pkSetSlotNull(PKVM* vm, int index) { CHECK_FIBER_EXISTS(vm); VALIDATE_SLOT_INDEX(index); @@ -654,6 +695,14 @@ PK_PUBLIC void pkSetSlotStringLength(PKVM* vm, int index, SET_SLOT(index, VAR_OBJ(newStringLength(vm, value, length))); } +void pkSetSlotStringFmt(PKVM* vm, int index, const char* fmt, ...) { + + va_list args; + va_start(args, fmt); + SET_SLOT(index, VAR_OBJ(newStringCFmt(vm, fmt, args))); + va_end(args); +} + void pkSetSlotHandle(PKVM* vm, int index, PkHandle* handle) { CHECK_FIBER_EXISTS(vm); VALIDATE_SLOT_INDEX(index); @@ -672,7 +721,50 @@ void pkSetGlobal(PKVM* vm, int module, int global, const char* name) { (void) moduleAddGlobal(vm, m, name, (uint32_t) strlen(name), SLOT(global)); } -#undef CHECK_FIBER_EXISTS +bool pkNewInstance(PKVM* vm, int cls, int index, int argc, int argv) { + CHECK_FIBER_EXISTS(vm); + VALIDATE_SLOT_INDEX(index); + + if (argc != 0) { + VALIDATE_SLOT_INDEX(argv); + VALIDATE_SLOT_INDEX(argv + argc - 1); + } + + ASSERT(IS_OBJ_TYPE(SLOT(cls), OBJ_CLASS), "Slot value wasn't a class."); + + Class* class_ = (Class*) AS_OBJ(SLOT(cls)); + Var instance = preConstructSelf(vm, class_); + + Closure* ctor = class_->ctor; + while (ctor == NULL) { + class_ = class_->super_class; + if (class_ == NULL) break; + ctor = class_->ctor; + } + + if (ctor != NULL) { + vmCallMethod(vm, instance, ctor, argc, vm->fiber->ret + argv, NULL); + } + + SET_SLOT(index, instance); + return !VM_HAS_ERROR(vm); +} + +void pkPlaceSelf(PKVM* vm, int index) { + CHECK_FIBER_EXISTS(vm); + VALIDATE_SLOT_INDEX(index); + SET_SLOT(index, vm->fiber->self); +} + +void pkGetClass(PKVM* vm, int instance, int index) { + CHECK_FIBER_EXISTS(vm); + VALIDATE_SLOT_INDEX(instance); + VALIDATE_SLOT_INDEX(index); + + SET_SLOT(index, VAR_OBJ(getClass(vm, SLOT(instance)))); +} + +#undef CHECK_RUNTIME #undef VALIDATE_ARGC #undef ERR_INVALID_ARG_TYPE #undef ARG diff --git a/src/pk_value.c b/src/pk_value.c index 5b2ade3..0725f36 100644 --- a/src/pk_value.c +++ b/src/pk_value.c @@ -274,6 +274,20 @@ String* newStringLength(PKVM* vm, const char* text, uint32_t length) { return string; } +String* newStringCFmt(PKVM* vm, const char* fmt, va_list args) { + va_list copy; + va_copy(copy, args); + int size = vsnprintf(NULL, 0, fmt, copy); + va_end(copy); + + // [size] is not including the null terminator so +1. + String* string = _allocateString(vm, (size_t) size + 1); + vsnprintf(string->data, string->capacity, fmt, args); + string->hash = utilHashString(string->data); + + return string; +} + List* newList(PKVM* vm, uint32_t size) { List* list = ALLOCATE(vm, List); vmPushTempRef(vm, &list->_super); diff --git a/src/pk_value.h b/src/pk_value.h index ae6ec2f..5e6fb4f 100644 --- a/src/pk_value.h +++ b/src/pk_value.h @@ -547,6 +547,8 @@ void varInitObject(Object* self, PKVM* vm, ObjectType type); String* newStringLength(PKVM* vm, const char* text, uint32_t length); +String* newStringCFmt(PKVM* vm, const char* fmt, va_list args); + // An inline function/macro implementation of newString(). Set below 0 to 1, to // make the implementation a static inline function, it's totally okey to // define a function inside a header as long as it's static (but not a fan). diff --git a/src/pk_vm.c b/src/pk_vm.c index ae7c1ff..a896bdf 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -1096,6 +1096,8 @@ L_do_call: // citizens. ASSERT(!IS_OBJ_TYPE(callable, OBJ_FUNC), OOPS); + *(fiber->ret) = VAR_NULL; //< Set the return value to null. + if (IS_OBJ_TYPE(callable, OBJ_CLOSURE)) { closure = (const Closure*)AS_OBJ(callable); @@ -1106,6 +1108,12 @@ L_do_call: fiber->self = preConstructSelf(vm, cls); CHECK_ERROR(); + // Note: + // For pocketlang instance the constructor will update self and return + // the instance (which might not be necessary since we're setting it + // here). + *fiber->ret = fiber->self; + closure = (const Closure*)(cls)->ctor; while (closure == NULL) { cls = cls->super_class; @@ -1120,7 +1128,6 @@ L_do_call: RUNTIME_ERROR(msg); } - *fiber->ret = fiber->self; fiber->self = VAR_UNDEFINED; DISPATCH(); } @@ -1142,8 +1149,6 @@ L_do_call: RUNTIME_ERROR(msg); } - *(fiber->ret) = VAR_NULL; //< Set the return value to null. - if (closure->fn->is_native) { if (closure->fn->native == NULL) { diff --git a/tests/modules/dummy.pk b/tests/modules/dummy.pk index 98c8e43..d6de094 100644 --- a/tests/modules/dummy.pk +++ b/tests/modules/dummy.pk @@ -5,7 +5,7 @@ from dummy import Dummy -d = Dummy() +d = Dummy(0) print(d) assert(d.val == 0) ## @getter @@ -19,5 +19,11 @@ assert(d >= 3.14) ## > and == overload assert(d.a_method(12, 34) == 408) ## method +d1 = Dummy(12) +d2 = Dummy(23) +d3 = d1 + d2 +assert(d3 is Dummy) +assert(d3.val == d1.val + d2.val) + # If we got here, that means all test were passed. print('All TESTS PASSED') diff --git a/tests/native/example2.c b/tests/native/example2.c index 43f2eab..3eab59c 100644 --- a/tests/native/example2.c +++ b/tests/native/example2.c @@ -3,8 +3,6 @@ * Distributed Under The MIT License */ -#error Native interface is being refactored and will be completed soon. - // This is an example on how to write your own custom type (Vector here) and // bind it with with the pocket VM. @@ -21,24 +19,18 @@ static const char* code = " \n" " v1 = Vec2(1, 2) \n" " print('v1 = $v1') \n" - " print() \n" - " \n" - " print('v1.x = ${v1.x}') \n" - " print('v1.y = ${v1.y}') \n" " print('v1.length = ${v1.length}') \n" " print() \n" " \n" " v1.x = 3; v1.y = 4; \n" - " print('v1.x = ${v1.x}') \n" - " print('v1.y = ${v1.y}') \n" + " print('v1 = $v1') \n" " print('v1.length = ${v1.length}') \n" " print() \n" " \n" " v2 = Vec2(5, 6) \n" + " print('v2 = $v2') \n" " v3 = v1 + v2 \n" - " print('v3 = ${v3}') \n" - " print('v3.x = ${v3.x}') \n" - " print('v3.y = ${v3.y}') \n" + " print('v3 = $v3') \n" " \n" ; @@ -72,7 +64,12 @@ void _vecGetter(PKVM* vm) { } else if (strcmp("y", name) == 0) { pkSetSlotNumber(vm, 0, self->y); return; + } else if (strcmp("length", name) == 0) { + double length = sqrt(pow(self->x, 2) + pow(self->y, 2)); + pkSetSlotNumber(vm, 0, length); + return; } + } void _vecSetter(PKVM* vm) { @@ -91,12 +88,42 @@ void _vecSetter(PKVM* vm) { } } +void _vecInit(PKVM* vm) { + double x, y; + if (!pkValidateSlotNumber(vm, 1, &x)) return; + if (!pkValidateSlotNumber(vm, 2, &y)) return; + + Vector* self = (Vector*) pkGetSelf(vm); + self->x = x; + self->y = y; +} + // Vec2 '+' operator method. void _vecAdd(PKVM* vm) { + Vector* self = (Vector*) pkGetSelf(vm); + + pkReserveSlots(vm, 5); // Now we have slots [0, 1, 2, 3, 4]. + + pkPlaceSelf(vm, 2); // slot[2] = self + pkGetClass(vm, 2, 2); // slot[2] = Vec2 class. + + // slot[1] is slot[2] == other is Vec2 ? + if (!pkValidateSlotInstanceOf(vm, 1, 2)) return; + Vector* other = (Vector*) pkGetSlotNativeInstance(vm, 1); + + // slot[3] = new.x + pkSetSlotNumber(vm, 3, self->x + other->x); + + // slot[4] = new.y + pkSetSlotNumber(vm, 4, self->y + other->y); + + // slot[0] = Vec2(slot[3], slot[4]) => return value. + if (!pkNewInstance(vm, 2, 0, 2, 3)) return; +} + +void _vecStr(PKVM* vm) { Vector* self = (Vector*)pkGetSelf(vm); - // FIXME: - // Temproarly it's not possible to get vector from the args since the native - // interface is being refactored. Will be implemented soon. + pkSetSlotStringFmt(vm, 0, "[%g, %g]", self->x, self->y); } // Register the 'Vector' module and it's functions. @@ -105,7 +132,12 @@ void registerVector(PKVM* vm) { PkHandle* Vec2 = pkNewClass(vm, "Vec2", NULL /*Base Class*/, vector, _newVec, _deleteVec); - pkClassAddMethod(vm, Vec2, "+", _vecAdd, 1); + + pkClassAddMethod(vm, Vec2, "@getter", _vecGetter, 1); + pkClassAddMethod(vm, Vec2, "@setter", _vecSetter, 2); + pkClassAddMethod(vm, Vec2, "_init", _vecInit, 2); + pkClassAddMethod(vm, Vec2, "_str", _vecStr, 0); + pkClassAddMethod(vm, Vec2, "+", _vecAdd, 1); pkReleaseHandle(vm, Vec2); pkRegisterModule(vm, vector);