Error if a class tries to inherit from null.

Fix #246.
This commit is contained in:
Bob Nystrom
2015-09-12 10:14:04 -07:00
parent e1f979e78a
commit 1a27593993
4 changed files with 32 additions and 39 deletions

View File

@ -1020,9 +1020,9 @@ static int emitJump(Compiler* compiler, Code instruction)
// Creates a new constant for the current value and emits the bytecode to load
// it from the constant table.
static void emitConstant(Compiler* compiler, Value value)
static void emitConstant(Compiler* compiler, Value value)
{
int constant = addConstant(compiler, value);
int constant = addConstant(compiler, value);
// Compile the code to load the constant.
emitShortArg(compiler, CODE_CONSTANT, constant);
@ -1790,6 +1790,15 @@ static void loadThis(Compiler* compiler)
}
}
// Pushes the value for a module-level variable implicitly imported from core.
static void loadCoreVariable(Compiler* compiler, const char* name)
{
int symbol = wrenSymbolTableFind(&compiler->parser->module->variableNames,
name, strlen(name));
ASSERT(symbol != -1, "Should have already defined core name.");
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, symbol);
}
// A parenthesized expression.
static void grouping(Compiler* compiler, bool allowAssignment)
{
@ -1800,13 +1809,8 @@ static void grouping(Compiler* compiler, bool allowAssignment)
// A list literal.
static void list(Compiler* compiler, bool allowAssignment)
{
// Load the List class.
int listClassSymbol = wrenSymbolTableFind(
&compiler->parser->module->variableNames, "List", 4);
ASSERT(listClassSymbol != -1, "Should have already defined 'List' variable.");
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, listClassSymbol);
// Instantiate a new list.
loadCoreVariable(compiler, "List");
callMethod(compiler, 0, "new()", 5);
// Compile the list elements. Each one compiles to a ".add()" call.
@ -1836,13 +1840,8 @@ static void list(Compiler* compiler, bool allowAssignment)
// A map literal.
static void map(Compiler* compiler, bool allowAssignment)
{
// Load the Map class.
int mapClassSymbol = wrenSymbolTableFind(
&compiler->parser->module->variableNames, "Map", 3);
ASSERT(mapClassSymbol != -1, "Should have already defined 'Map' variable.");
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, mapClassSymbol);
// Instantiate a new map.
loadCoreVariable(compiler, "Map");
callMethod(compiler, 0, "new()", 5);
// Compile the map elements. Each one is compiled to just invoke the
@ -2125,7 +2124,7 @@ static void null(Compiler* compiler, bool allowAssignment)
// A number or string literal.
static void literal(Compiler* compiler, bool allowAssignment)
{
emitConstant(compiler, compiler->parser->value);
emitConstant(compiler, compiler->parser->value);
}
static void super_(Compiler* compiler, bool allowAssignment)
@ -3002,8 +3001,8 @@ static bool method(Compiler* compiler, ClassCompiler* classCompiler,
if (isForeign)
{
// Define a constant for the signature.
emitConstant(compiler, wrenNewString(compiler->parser->vm,
fullSignature, length));
emitConstant(compiler, wrenNewString(compiler->parser->vm,
fullSignature, length));
// We don't need the function we started compiling in the parameter list
// any more.
@ -3042,8 +3041,8 @@ static void classDefinition(Compiler* compiler, bool isForeign)
int slot = declareNamedVariable(compiler);
// Make a string constant for the name.
emitConstant(compiler, wrenNewString(compiler->parser->vm,
compiler->parser->previous.start, compiler->parser->previous.length));
emitConstant(compiler, wrenNewString(compiler->parser->vm,
compiler->parser->previous.start, compiler->parser->previous.length));
// Load the superclass (if there is one).
if (match(compiler, TOKEN_IS))
@ -3052,8 +3051,8 @@ static void classDefinition(Compiler* compiler, bool isForeign)
}
else
{
// Create the empty class.
emit(compiler, CODE_NULL);
// Implicitly inherit from Object.
loadCoreVariable(compiler, "Object");
}
// Store a placeholder for the number of fields argument. We don't know

View File

@ -164,13 +164,12 @@ OPCODE(CONSTRUCT)
// compiler-generated constructor metaclass methods.
OPCODE(FOREIGN_CONSTRUCT)
// Creates a class. Top of stack is the superclass, or `null` if the class
// inherits Object. Below that is a string for the name of the class. Byte
// [arg] is the number of fields in the class.
// Creates a class. Top of stack is the superclass. Below that is a string for
// the name of the class. Byte [arg] is the number of fields in the class.
OPCODE(CLASS)
// Creates a foreign class. Top of stack is the superclass, or `null` if the
// class inherits Object. Below that is a string for the name of the class.
// Creates a foreign class. Top of stack is the superclass. Below that is a
// string for the name of the class.
OPCODE(FOREIGN_CLASS)
// Define a method for symbol [arg]. The class receiving the method is popped

View File

@ -639,27 +639,21 @@ static bool defineClass(WrenVM* vm, ObjFiber* fiber, int numFields,
{
// Pull the name and superclass off the stack.
Value name = fiber->stackTop[-2];
Value superclassValue = fiber->stackTop[-1];
Value superclass = fiber->stackTop[-1];
// We have two values on the stack and we are going to leave one, so discard
// the other slot.
fiber->stackTop--;
// Use implicit Object superclass if none given.
ObjClass* superclass = vm->objectClass;
if (!IS_NULL(superclassValue))
Value error = validateSuperclass(vm, name, superclass, numFields);
if (!IS_NULL(error))
{
Value error = validateSuperclass(vm, name, superclassValue, numFields);
if (!IS_NULL(error))
{
fiber->stackTop[-1] = error;
return false;
}
superclass = AS_CLASS(superclassValue);
fiber->stackTop[-1] = error;
return false;
}
ObjClass* classObj = wrenNewClass(vm, superclass, numFields, AS_STRING(name));
ObjClass* classObj = wrenNewClass(vm, AS_CLASS(superclass), numFields,
AS_STRING(name));
fiber->stackTop[-1] = OBJ_VAL(classObj);
if (numFields == -1) bindForeignClass(vm, classObj, module);

View File

@ -0,0 +1 @@
class Foo is null {} // expect runtime error: Class 'Foo' cannot inherit from a non-class object.