Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Design] reconstruction magic methods #281

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/core/compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -2802,7 +2802,7 @@ static void compileFunction(Compiler* compiler, FuncType fn_type) {
global_index = compilerAddVariable(compiler, name, name_length, name_line);
}

if (fn_type == FUNC_METHOD && strncmp(name, CTOR_NAME, name_length) == 0) {
if (fn_type == FUNC_METHOD && strncmp(name, LITS__init, name_length) == 0) {
fn_type = FUNC_CONSTRUCTOR;
}

Expand Down
123 changes: 88 additions & 35 deletions src/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,22 +182,15 @@ String* varToString(PKVM* vm, Var self, bool repr) {
// from GC).
Closure* closure = NULL;

bool has_method = false;
if (!repr) {
String* name = newString(vm, LITS__str); // TODO: static vm string?.
vmPushTempRef(vm, &name->_super); // name.
has_method = hasMethod(vm, self, name, &closure);
vmPopTempRef(vm); // name.
closure = getMagicMethod(getClass(vm, self), METHOD_STR);
}

if (!has_method) {
String* name = newString(vm, LITS__repr); // TODO: static vm string?.
vmPushTempRef(vm, &name->_super); // name.
has_method = hasMethod(vm, self, name, &closure);
vmPopTempRef(vm); // name.
if (closure == NULL) {
closure = getMagicMethod(getClass(vm, self), METHOD_REPR);
}

if (has_method) {
if (closure != NULL) {
Var ret = VAR_NULL;
PkResult result = vmCallMethod(vm, self, closure, 0, NULL, &ret);
if (result != PK_RESULT_SUCCESS) return NULL;
Expand Down Expand Up @@ -976,6 +969,32 @@ DEF(_objRepr,
RET(VAR_OBJ(toRepr(vm, SELF)));
}

DEF(_objGetattr,
"Object.getattr(name:String[, skipGetter: bool]) -> Var",
"Returns the value of the named attribute of an object.") {

if (!pkCheckArgcRange(vm, ARGC, 1, 2)) return;

String* name;
if (!validateArgString(vm, 1, &name)) return;

bool skipGetter = (ARGC >= 2 ? toBool(ARG(2)) : false);
RET(varGetAttrib(vm, SELF, name, skipGetter));
}

DEF(_objSetattr,
"Object.setattr(name:String, value:Var[, skipSetter: bool]) -> Null",
"Sets the value of the attribute of an object.") {

if (!pkCheckArgcRange(vm, ARGC, 2, 3)) return;

String* name;
if (!validateArgString(vm, 1, &name)) return;

bool skipSetter = (ARGC >= 3 ? toBool(ARG(3)) : false);
varSetAttrib(vm, SELF, name, ARG(2), skipSetter);
}

DEF(_numberTimes,
"Number.times(f:Closure)",
"Iterate the function [f] n times. Here n is the integral value of the "
Expand Down Expand Up @@ -1422,7 +1441,8 @@ static void initializePrimitiveClasses(PKVM* vm) {
fn->native = ptr; \
fn->arity = arity_; \
vmPushTempRef(vm, &fn->_super); /* fn. */ \
vm->builtin_classes[type]->ctor = newClosure(vm, fn); \
vm->builtin_classes[type]->magic_methods[METHOD_INIT] = \
newClosure(vm, fn); \
vmPopTempRef(vm); /* fn. */ \
} while (false)

Expand Down Expand Up @@ -1453,6 +1473,9 @@ static void initializePrimitiveClasses(PKVM* vm) {
ADD_METHOD(PK_OBJECT, "typename", _objTypeName, 0);
ADD_METHOD(PK_OBJECT, "_repr", _objRepr, 0);

ADD_METHOD(PK_OBJECT, "getattr", _objGetattr, -1);
ADD_METHOD(PK_OBJECT, "setattr", _objSetattr, -1);

ADD_METHOD(PK_NUMBER, "times", _numberTimes, 1);
ADD_METHOD(PK_NUMBER, "isint", _numberIsint, 0);
ADD_METHOD(PK_NUMBER, "isbyte", _numberIsbyte, 0);
Expand Down Expand Up @@ -1536,6 +1559,49 @@ Var preConstructSelf(PKVM* vm, Class* cls) {
return VAR_NULL;
}

void bindMethod(PKVM* vm, Class* cls, Closure* method) {
// TODO: check hash instead of using strcmp?
if (strcmp(method->fn->name, LITS__init) == 0) {
cls->magic_methods[METHOD_INIT] = method;
} else if (strcmp(method->fn->name, LITS__str) == 0) {
cls->magic_methods[METHOD_STR] = method;
} else if (strcmp(method->fn->name, LITS__repr) == 0) {
cls->magic_methods[METHOD_REPR] = method;
} else if (strcmp(method->fn->name, LITS__getter) == 0) {
cls->magic_methods[METHOD_GETTER] = method;
} else if (strcmp(method->fn->name, LITS__setter) == 0) {
cls->magic_methods[METHOD_SETTER] = method;
} else if (strcmp(method->fn->name, LITS__call) == 0) {
cls->magic_methods[METHOD_CALL] = method;
}

pkClosureBufferWrite(&cls->methods, vm, method);
}

Closure* getMagicMethod(Class* cls, MagicMethod m) {
ASSERT(cls != NULL, OOPS);

// magic method
// -1: find the method from ancestor
// NULL: not found and don't find again
if (cls->magic_methods[m] == (Closure*) -1) {
cls->magic_methods[m] = NULL;

Class* super = cls->super_class;
while (super != NULL) {
if (super->magic_methods[m] != NULL &&
super->magic_methods[m] != (Closure*) -1) {

cls->magic_methods[m] = super->magic_methods[m];
break;
}
super = super->super_class;
}
}
// printf("%d %p\n", m, cls->magic_methods[m]);
return cls->magic_methods[m];
}

Class* getClass(PKVM* vm, Var instance) {
PkVarType type = getVarType(instance);
if (0 <= type && type < PK_INSTANCE) {
Expand Down Expand Up @@ -1587,7 +1653,7 @@ Var getMethod(PKVM* vm, Var self, String* name, bool* is_method) {

// If the attribute not found it'll set an error.
if (is_method) *is_method = false;
return varGetAttrib(vm, self, name);
return varGetAttrib(vm, self, name, false);
}

Closure* getSuperMethod(PKVM* vm, Var self, String* name) {
Expand Down Expand Up @@ -1961,7 +2027,7 @@ bool varIsType(PKVM* vm, Var inst, Var type) {
return false;
}

Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
Var varGetAttrib(PKVM* vm, Var on, String* attrib, bool skipGetter) {

#define ERR_NO_ATTRIB(vm, on, attrib) \
VM_SET_ERROR(vm, stringFormat(vm, "'$' object has no attribute named '$'.", \
Expand Down Expand Up @@ -2130,16 +2196,9 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
Instance* inst = (Instance*)obj;
Var value = VAR_NULL;

if (inst->native != NULL) {

Closure* getter;
// TODO: static vm string?
String* getter_name = newString(vm, GETTER_NAME);
vmPushTempRef(vm, &getter_name->_super); // getter_name.
bool has_getter = hasMethod(vm, on, getter_name, &getter);
vmPopTempRef(vm); // getter_name.

if (has_getter) {
if (!skipGetter) {
Closure* getter = getMagicMethod(inst->cls, METHOD_GETTER);
if (getter != NULL) {
Var attrib_name = VAR_OBJ(attrib);
vmCallMethod(vm, on, getter, 1, &attrib_name, &value);
return value; // If any error occure, it was already set.
Expand All @@ -2165,7 +2224,8 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
#undef ERR_NO_ATTRIB
}

void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) {
void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value, bool skipSetter)
{

// Set error for accessing non-existed attribute.
#define ERR_NO_ATTRIB(vm, on, attrib) \
Expand Down Expand Up @@ -2197,18 +2257,11 @@ void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) {
}

case OBJ_INST: {

Instance* inst = (Instance*)obj;
if (inst->native != NULL) {
Closure* setter;
// TODO: static vm string?
String* setter_name = newString(vm, SETTER_NAME);
vmPushTempRef(vm, &setter_name->_super); // setter_name.
bool has_setter = hasMethod(vm, VAR_OBJ(inst), setter_name, &setter);
vmPopTempRef(vm); // setter_name.

if (has_setter) {

if (!skipSetter) {
Closure* setter = getMagicMethod(inst->cls, METHOD_SETTER);
if (setter != NULL) {
// FIXME:
// Once we retreive values from directly the stack we can pass the
// args pointer, pointing in the VM stack, instead of creating a temp
Expand Down
22 changes: 11 additions & 11 deletions src/core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#define LITS__init "_init"
#define LITS__str "_str"
#define LITS__repr "_repr"
#define LITS__getter "_getter"
#define LITS__setter "_setter"
#define LITS__call "_call"

// Functions, methods, classes and other names which are intrenal / special to
// pocketlang are starts with the following character (ex: @main, @literalFn).
Expand All @@ -33,15 +36,6 @@
// they're uniquely identified by their index in the script's function buffer.
#define LITERAL_FN_NAME "@func"

// Name of a constructor function.
#define CTOR_NAME LITS__init

// Getter/Setter method names used by the native instance to get/ set value.
// Script instance's values doesn't support methods but they use vanila
// '.attrib', '.attrib=' operators.
#define GETTER_NAME "@getter"
#define SETTER_NAME "@setter"

// Initialize core language, builtin function and core libs.
void initializeCore(PKVM* vm);

Expand Down Expand Up @@ -78,6 +72,12 @@ void moduleAddFunctionInternal(PKVM* vm, Module* module,
// For other classes the return value will be an Instance.
Var preConstructSelf(PKVM* vm, Class* cls);

// Bind a method to a class and deal with magic methods.
void bindMethod(PKVM* vm, Class* cls, Closure* method);

// Get the specified magic method or NULL. Cache the method if possible.
Closure* getMagicMethod(Class* cls, MagicMethod mm);

// Returns the class of the [instance].
Class* getClass(PKVM* vm, Var instance);

Expand Down Expand Up @@ -137,11 +137,11 @@ bool varContains(PKVM* vm, Var elem, Var container);
bool varIsType(PKVM* vm, Var inst, Var type);

// Returns the attribute named [attrib] on the variable [on].
Var varGetAttrib(PKVM* vm, Var on, String* attrib);
Var varGetAttrib(PKVM* vm, Var on, String* attrib, bool skipGetter);

// Set the attribute named [attrib] on the variable [on] with the given
// [value].
void varSetAttrib(PKVM* vm, Var on, String* name, Var value);
void varSetAttrib(PKVM* vm, Var on, String* name, Var value, bool skipSetter);

// Returns the subscript value (ie. on[key]).
Var varGetSubscript(PKVM* vm, Var on, Var key);
Expand Down
32 changes: 16 additions & 16 deletions src/core/public.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,9 @@ void pkClassAddMethod(PKVM* vm, PkHandle* cls,

Closure* method = newClosure(vm, fn);
vmPopTempRef(vm); // fn.

vmPushTempRef(vm, &method->_super); // method.
{
pkClosureBufferWrite(&class_->methods, vm, method);
if (!strcmp(name, CTOR_NAME)) class_->ctor = method;
}
bindMethod(vm, class_, method);
vmPopTempRef(vm); // method.
}

Expand Down Expand Up @@ -457,7 +455,6 @@ static inline bool isStringEmpty(const char* line) {
// This function will get the main function from the module to run it in the
// repl mode.
Closure* moduleGetMainFunction(PKVM* vm, Module* module) {

int main_index = moduleGetGlobalIndex(module, IMPLICIT_MAIN_NAME,
(uint32_t) strlen(IMPLICIT_MAIN_NAME));
if (main_index == -1) return NULL;
Expand Down Expand Up @@ -842,7 +839,7 @@ bool pkSetAttribute(PKVM* vm, int instance, const char* name, int value) {

String* sname = newString(vm, name);
vmPushTempRef(vm, &sname->_super); // sname.
varSetAttrib(vm, SLOT(instance), sname, SLOT(value));
varSetAttrib(vm, SLOT(instance), sname, SLOT(value), true);
vmPopTempRef(vm); // sname.

return !VM_HAS_ERROR(vm);
Expand All @@ -857,7 +854,7 @@ bool pkGetAttribute(PKVM* vm, int instance, const char* name,

String* sname = newString(vm, name);
vmPushTempRef(vm, &sname->_super); // sname.
SET_SLOT(index, varGetAttrib(vm, SLOT(instance), sname));
SET_SLOT(index, varGetAttrib(vm, SLOT(instance), sname, true));
vmPopTempRef(vm); // sname.

return !VM_HAS_ERROR(vm);
Expand All @@ -867,17 +864,20 @@ static Var _newInstance(PKVM* vm, Class* cls, int argc, Var* argv) {
Var instance = preConstructSelf(vm, cls);
if (VM_HAS_ERROR(vm)) return VAR_NULL;

if (IS_OBJ(instance)) vmPushTempRef(vm, AS_OBJ(instance)); // instance.

Closure* ctor = cls->ctor;
while (ctor == NULL) {
cls = cls->super_class;
if (cls == NULL) break;
ctor = cls->ctor;
bool pushed = false;
if (IS_OBJ(instance)) {
vmPushTempRef(vm, AS_OBJ(instance)); // instance.
pushed = true;
}

if (ctor != NULL) vmCallMethod(vm, instance, ctor, argc, argv, NULL);
if (IS_OBJ(instance)) vmPopTempRef(vm); // instance.
Closure* init = getMagicMethod(cls, METHOD_INIT);
if (init != NULL) {
// for builtin classes, preConstructSelf returns null,
// and instance is returned by _init.
vmCallMethod(vm, instance, init, argc, argv,
IS_NULL(instance) ? &instance : NULL);
}
if (pushed) vmPopTempRef(vm); // instance.

return instance;
}
Expand Down
7 changes: 6 additions & 1 deletion src/core/value.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,9 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) {
Class* cls = (Class*)obj;
vm->bytes_allocated += sizeof(Class);
markObject(vm, &cls->owner->_super);
markObject(vm, &cls->ctor->_super);
markObject(vm, &cls->name->_super);
markObject(vm, &cls->static_attribs->_super);
// don't need to mark magic_methods, they are all in cls->methods.

markClosureBuffer(vm, &cls->methods);
vm->bytes_allocated += sizeof(Closure) * cls->methods.capacity;
Expand Down Expand Up @@ -527,6 +527,11 @@ Class* newClass(PKVM* vm, const char* name, int length,
cls->super_class = super;
cls->docstring = docstring;

// Initialize to -1 as undefined
for (int i = 0; i < MAX_MAGIC_METHODS; i++) {
cls->magic_methods[i] = (Closure*)-1;
}

// Builtin types doesn't belongs to a module.
if (module != NULL) {
cls->name = moduleAddString(module, vm, name, length, NULL);
Expand Down
14 changes: 12 additions & 2 deletions src/core/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,16 @@ struct Fiber {
String* error;
};

typedef enum {
METHOD_INIT,
METHOD_STR,
METHOD_REPR,
METHOD_GETTER,
METHOD_SETTER,
METHOD_CALL,
MAX_MAGIC_METHODS,
} MagicMethod;

struct Class {
Object _super;

Expand All @@ -538,7 +548,8 @@ struct Class {
// builtin type's class.
PkVarType class_of;

Closure* ctor; //< The constructor function.
// Magic methods, ctor/getter/setter etc.
Closure* magic_methods[MAX_MAGIC_METHODS];

// A buffer of methods of the class.
pkClosureBuffer methods;
Expand All @@ -550,7 +561,6 @@ struct Class {
// For script/ builtin types it'll be NULL.
pkNewInstanceFn new_fn;
pkDeleteInstanceFn delete_fn;

};

typedef struct {
Expand Down
Loading