Statically dispatch super() calls. Fix #250.

This commit is contained in:
Bob Nystrom
2015-06-02 07:33:39 -07:00
parent 06feba4861
commit 3cdfaea797
3 changed files with 60 additions and 19 deletions

View File

@ -1640,6 +1640,20 @@ static void callSignature(Compiler* compiler, Code instruction,
{
int symbol = signatureSymbol(compiler, signature);
emitShortArg(compiler, (Code)(instruction + signature->arity), symbol);
if (instruction == CODE_SUPER_0)
{
// Super calls need to be statically bound to the class's superclass. This
// ensures we call the right method even when a method containing a super
// call is inherited by another subclass.
//
// We bind it at class definition time by storing a reference to the
// superclass in a constant. So, here, we create a slot in the constant
// table and store NULL in it. When the method is bound, we'll look up the
// superclass then and store it in the constant slot.
int constant = addConstant(compiler, NULL_VAL);
emitShort(compiler, constant);
}
}
// Compiles a method call with [numArgs] for a method with [name] with [length].
@ -2592,6 +2606,16 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
case CODE_CALL_14:
case CODE_CALL_15:
case CODE_CALL_16:
case CODE_JUMP:
case CODE_LOOP:
case CODE_JUMP_IF:
case CODE_AND:
case CODE_OR:
case CODE_METHOD_INSTANCE:
case CODE_METHOD_STATIC:
case CODE_LOAD_MODULE:
return 2;
case CODE_SUPER_0:
case CODE_SUPER_1:
case CODE_SUPER_2:
@ -2609,16 +2633,6 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
case CODE_SUPER_14:
case CODE_SUPER_15:
case CODE_SUPER_16:
case CODE_JUMP:
case CODE_LOOP:
case CODE_JUMP_IF:
case CODE_AND:
case CODE_OR:
case CODE_METHOD_INSTANCE:
case CODE_METHOD_STATIC:
case CODE_LOAD_MODULE:
return 2;
case CODE_IMPORT_VARIABLE:
return 4;
@ -3224,6 +3238,33 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
fn->bytecode[ip++] += classObj->superclass->numFields;
break;
case CODE_SUPER_0:
case CODE_SUPER_1:
case CODE_SUPER_2:
case CODE_SUPER_3:
case CODE_SUPER_4:
case CODE_SUPER_5:
case CODE_SUPER_6:
case CODE_SUPER_7:
case CODE_SUPER_8:
case CODE_SUPER_9:
case CODE_SUPER_10:
case CODE_SUPER_11:
case CODE_SUPER_12:
case CODE_SUPER_13:
case CODE_SUPER_14:
case CODE_SUPER_15:
case CODE_SUPER_16:
{
// Skip over the symbol.
ip += 2;
// Fill in the constant slot with a reference to the superclass.
int constant = (fn->bytecode[ip] << 8) | fn->bytecode[ip + 1];
fn->constants[constant] = OBJ_VAL(classObj->superclass);
break;
}
case CODE_CLOSURE:
{
// Bind the nested closure too.

View File

@ -215,8 +215,9 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
{
int numArgs = bytecode[i - 1] - CODE_SUPER_0;
int symbol = READ_SHORT();
printf("SUPER_%-10d %5d '%s'\n", numArgs, symbol,
vm->methodNames.data[symbol].buffer);
int superclass = READ_SHORT();
printf("SUPER_%-10d %5d '%s' %5d\n", numArgs, symbol,
vm->methodNames.data[symbol].buffer, superclass);
break;
}

View File

@ -840,12 +840,10 @@ static bool runInterpreter(WrenVM* vm)
int numArgs = instruction - CODE_SUPER_0 + 1;
int symbol = READ_SHORT();
// The receiver is the first argument.
Value* args = fiber->stackTop - numArgs;
ObjClass* classObj = wrenGetClassInline(vm, args[0]);
// Ignore methods defined on the receiver's immediate class.
classObj = classObj->superclass;
// The superclass is stored in a constant.
ObjClass* classObj = AS_CLASS(fn->constants[READ_SHORT()]);
// If the class's method table doesn't include the symbol, bail.
if (symbol >= classObj->methods.count)
@ -1218,9 +1216,10 @@ static bool runInterpreter(WrenVM* vm)
// or a runtime error.
UNREACHABLE();
return false;
#undef READ_BYTE
#undef READ_SHORT
}
#undef READ_BYTE
#undef READ_SHORT
// Creates an [ObjFn] that invokes a method with [signature] when called.
static ObjFn* makeCallStub(WrenVM* vm, ObjModule* module, const char* signature)