diff --git a/src/vm/wren_compiler.c b/src/vm/wren_compiler.c index 24a3e945..c8030205 100644 --- a/src/vm/wren_compiler.c +++ b/src/vm/wren_compiler.c @@ -3082,6 +3082,17 @@ static void defineMethod(Compiler* compiler, Variable classVariable, emitShortArg(compiler, instruction, methodSymbol); } +// Used by `method` function below to find a method in the method list +bool hasMethod(IntBuffer* buffer, int data) +{ + for (int i = 0; i < buffer->count; i++) + { + if (memcmp(&buffer->data[i], &data, sizeof(data)) == 0) return true; + } + + return false; +} + // Compiles a method definition inside a class body. // // Returns `true` if it compiled successfully, or `false` if the method couldn't @@ -3159,13 +3170,12 @@ static bool method(Compiler* compiler, Variable classVariable) IntBuffer* methods = isStatic ? &compiler->enclosingClass->staticMethods : &compiler->enclosingClass->instanceMethods; - int symbol = wrenIntBufferFind(methods, methodSymbol); - if (symbol != -1) + // Check if the method table already contains this symbol + if (hasMethod(methods, methodSymbol)) { const char* methodType = isStatic ? "static method" : "method"; - error(compiler, "Already defined %s '%s' for class %s in module '%s'.", - methodType, fullSignature, &compiler->enclosingClass->name->value, - compiler->parser->module->name); + error(compiler, "Already defined %s '%s' for class %s.", + methodType, fullSignature, &compiler->enclosingClass->name->value); } wrenIntBufferWrite(compiler->parser->vm, methods, methodSymbol); @@ -3181,12 +3191,15 @@ static void classDefinition(Compiler* compiler, bool isForeign) classVariable.scope = compiler->scopeDepth == -1 ? SCOPE_MODULE : SCOPE_LOCAL; classVariable.index = declareNamedVariable(compiler); - ObjString* className = AS_STRING(wrenNewString(compiler->parser->vm, - compiler->parser->previous.start, compiler->parser->previous.length)); + // Create shared class name value + Value classNameString = wrenNewString(compiler->parser->vm, + compiler->parser->previous.start, compiler->parser->previous.length); + + // Create class name string to track method duplicates + ObjString* className = AS_STRING(classNameString); // Make a string constant for the name. - emitConstant(compiler, wrenNewString(compiler->parser->vm, - compiler->parser->previous.start, compiler->parser->previous.length)); + emitConstant(compiler, classNameString); // Load the superclass (if there is one). if (match(compiler, TOKEN_IS)) @@ -3229,6 +3242,8 @@ static void classDefinition(Compiler* compiler, bool isForeign) // bytecode will be adjusted by [wrenBindMethod] to take inherited fields // into account. wrenSymbolTableInit(&classCompiler.fields); + + // Set up int buffers to track duplicate static and instance methods wrenIntBufferInit(&classCompiler.staticMethods); wrenIntBufferInit(&classCompiler.instanceMethods); compiler->enclosingClass = &classCompiler; @@ -3254,6 +3269,8 @@ static void classDefinition(Compiler* compiler, bool isForeign) (uint8_t)classCompiler.fields.count; } + // Clear symbol table for tracking field names + // and int buffers for tracking duplicate methods wrenSymbolTableClear(compiler->parser->vm, &classCompiler.fields); wrenIntBufferClear(compiler->parser->vm, &classCompiler.staticMethods); wrenIntBufferClear(compiler->parser->vm, &classCompiler.instanceMethods); diff --git a/src/vm/wren_utils.h b/src/vm/wren_utils.h index 1ea5fc68..4ec04a37 100644 --- a/src/vm/wren_utils.h +++ b/src/vm/wren_utils.h @@ -27,8 +27,7 @@ typedef struct { void wren##name##BufferClear(WrenVM* vm, name##Buffer* buffer); \ void wren##name##BufferFill(WrenVM* vm, name##Buffer* buffer, type data, \ int count); \ - void wren##name##BufferWrite(WrenVM* vm, name##Buffer* buffer, type data); \ - int wren##name##BufferFind(name##Buffer* buffer, type data) + void wren##name##BufferWrite(WrenVM* vm, name##Buffer* buffer, type data) // This should be used once for each type instantiation, somewhere in a .c file. #define DEFINE_BUFFER(name, type) \ @@ -65,15 +64,6 @@ typedef struct { void wren##name##BufferWrite(WrenVM* vm, name##Buffer* buffer, type data) \ { \ wren##name##BufferFill(vm, buffer, data, 1); \ - } \ - int wren##name##BufferFind(name##Buffer* buffer, type data) \ - { \ - for (int i = 0; i < buffer->count; i++) \ - { \ - if (memcmp(&buffer->data[i], &data, sizeof(data)) == 0) return i; \ - } \ - \ - return -1; \ } DECLARE_BUFFER(Byte, uint8_t); diff --git a/test/language/method/duplicate_methods.wren b/test/language/method/duplicate_methods.wren new file mode 100644 index 00000000..00a72a3b --- /dev/null +++ b/test/language/method/duplicate_methods.wren @@ -0,0 +1,11 @@ +class Foo { + construct new() {} + foo() {} + foo() {} // expect error + + static bar() {} + static bar() {} // expect error + + baz() {} + static baz() {} +}