|
|
@@ -71,6 +71,10 @@ void initVM() {
|
|
|
initTable(&vm.globals);
|
|
|
initTable(&vm.strings);
|
|
|
|
|
|
+ // 类初始化方法
|
|
|
+ vm.initString = NULL;
|
|
|
+ vm.initString = copyString("init", 4);
|
|
|
+
|
|
|
/// Native Function define.
|
|
|
defineNative("clock", clockNative);
|
|
|
}
|
|
|
@@ -100,7 +104,7 @@ static bool callValue(Value callee, int argCount) {
|
|
|
switch (OBJ_TYPE(callee)) {
|
|
|
case OBJ_BOUND_METHOD: {
|
|
|
ObjBoundMethod *bound = AS_BOUND_METHOD(callee);
|
|
|
- vm.stackTop[-argCount-1] = bound->receiver;
|
|
|
+ vm.stackTop[-argCount - 1] = bound->receiver;
|
|
|
return call(bound->method, argCount);
|
|
|
}
|
|
|
case OBJ_CLOSURE:
|
|
|
@@ -114,7 +118,21 @@ static bool callValue(Value callee, int argCount) {
|
|
|
}
|
|
|
case OBJ_CLASS: {
|
|
|
ObjClass *klass = AS_CLASS(callee);
|
|
|
+ // stack 中 class 替换成 instance
|
|
|
vm.stackTop[-argCount - 1] = OBJ_VAL(newInstance(klass));
|
|
|
+ // 初始化方法
|
|
|
+ // class Brunch {
|
|
|
+ // init(food, drink) {}
|
|
|
+ //}
|
|
|
+ //
|
|
|
+ //Brunch("eggs", "coffee");
|
|
|
+ Value initializer;
|
|
|
+ if (tableGet(&klass->methods, vm.initString, &initializer)) {
|
|
|
+ return call(AS_CLOSURE(initializer), argCount);
|
|
|
+ } else if (argCount != 0) {
|
|
|
+ runtimeError("Expected 0 arguments but got %d.", argCount);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
return true;
|
|
|
}
|
|
|
default:
|
|
|
@@ -190,6 +208,30 @@ static bool bindKlassMethod(ObjClass *klass, ObjString *name) {
|
|
|
push(OBJ_VAL(bound));
|
|
|
return true;
|
|
|
}
|
|
|
+static bool invokeFromClas(ObjClass *klass, ObjString *name, int argCount) {
|
|
|
+ Value method;
|
|
|
+ if (!tableGet(&klass->methods, name, &method)) {
|
|
|
+ runtimeError("Undefined property '%s'.", name->chars);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return call(AS_CLOSURE(method), argCount);
|
|
|
+}
|
|
|
+static bool invoke(ObjString *name, int argCount) {
|
|
|
+ Value receiver = peek(argCount);
|
|
|
+ if (!IS_INSTANCE(receiver)) {
|
|
|
+ runtimeError("Only instances have methods.");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ ObjInstance *instance = AS_INSTANCE(receiver);
|
|
|
+
|
|
|
+ Value value;
|
|
|
+ if (tableGet(&instance->fields, name, &value)) {
|
|
|
+ vm.stackTop[-argCount - 1] = value;
|
|
|
+ return callValue(value, argCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ return invokeFromClas(instance->klass, name, argCount);
|
|
|
+}
|
|
|
/// VM run function - exec opcode
|
|
|
/// \return
|
|
|
static InterpretResult run() {
|
|
|
@@ -407,6 +449,15 @@ static InterpretResult run() {
|
|
|
push(value);
|
|
|
break;
|
|
|
}
|
|
|
+ case OP_INVOKE: {
|
|
|
+ ObjString *method = READ_STRING();
|
|
|
+ int argCount = READ_BYTE();
|
|
|
+ if (!invoke(method, argCount)) {
|
|
|
+ return INTERPRET_RUNTIME_ERROR;
|
|
|
+ }
|
|
|
+ frame = &vm.frames[vm.frameCount - 1];
|
|
|
+ break;
|
|
|
+ }
|
|
|
case OP_CLOSURE: {
|
|
|
ObjFunction *function = AS_FUNCTION(READ_CONSTANT());
|
|
|
ObjClosure *closure = newClosure(function);
|
|
|
@@ -465,6 +516,7 @@ static InterpretResult run() {
|
|
|
void freeVM() {
|
|
|
freeTable(&vm.globals);
|
|
|
freeTable(&vm.strings);
|
|
|
+ vm.initString = NULL;
|
|
|
freeObjects();
|
|
|
}
|
|
|
|