Merge pull request #229 from ThakeeNathees/native-api

native instance interface implemented
This commit is contained in:
Thakee Nathees 2022-05-17 08:03:00 +05:30 committed by GitHub
commit 80896f04aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 233 additions and 23 deletions

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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).

View File

@ -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) {

View File

@ -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')

View File

@ -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);
// FIXME:
// Temproarly it's not possible to get vector from the args since the native
// interface is being refactored. Will be implemented soon.
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);
pkSetSlotStringFmt(vm, 0, "[%g, %g]", self->x, self->y);
}
// Register the 'Vector' module and it's functions.
@ -105,6 +132,11 @@ void registerVector(PKVM* vm) {
PkHandle* Vec2 = pkNewClass(vm, "Vec2", NULL /*Base Class*/,
vector, _newVec, _deleteVec);
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);