Skip to content

Instantly share code, notes, and snippets.

@munificent
Created December 11, 2013 15:58
Show Gist options
  • Save munificent/7912965 to your computer and use it in GitHub Desktop.
Save munificent/7912965 to your computer and use it in GitHub Desktop.
Patch for generating default constructors at compile time. For mysterious reasons, it causes the method_call benchmark perf to degrade by ~10%.
diff --git a/src/wren_compiler.c b/src/wren_compiler.c
index cbb9ca6..83420f1 100644
--- a/src/wren_compiler.c
+++ b/src/wren_compiler.c
@@ -1600,6 +1600,24 @@ void block(Compiler* compiler)
emit(compiler, CODE_POP);
}
+// Defines a default constructor method with an empty body.
+static void defaultConstructor(Compiler* compiler)
+{
+ Compiler ctorCompiler;
+ int constant = initCompiler(&ctorCompiler, compiler->parser, compiler, 1);
+
+ // Just return the receiver which is in the first local slot.
+ emit(&ctorCompiler, CODE_LOAD_LOCAL);
+ emit(&ctorCompiler, 0);
+ emit(&ctorCompiler, CODE_RETURN);
+
+ endCompiler(&ctorCompiler, constant);
+
+ // Define the constructor method.
+ emit(compiler, CODE_METHOD_CTOR);
+ emit(compiler, ensureSymbol(&compiler->parser->vm->methods, "new", 3));
+}
+
// Compiles a statement. These can only appear at the top-level or within
// curly blocks. Unlike expressions, these do not leave a value on the stack.
void statement(Compiler* compiler)
@@ -1626,20 +1644,20 @@ void statement(Compiler* compiler)
// used.
int numFieldsInstruction = emit(compiler, 255);
- // Compile the method definitions.
- consume(compiler, TOKEN_LEFT_BRACE, "Expect '}' after class body.");
-
- // Set up a symbol table for the class's fields.
+ // Set up a symbol table for the class's fields. We'll initially compile
+ // them to slots starting at zero. When the method is bound to the close
+ // the bytecode will be adjusted by [wrenBindMethod] to take inherited
+ // fields into account.
SymbolTable* previousFields = compiler->fields;
SymbolTable fields;
initSymbolTable(&fields);
compiler->fields = &fields;
- // TODO(bob): Need to handle inherited fields. Ideally, a subclass's fields
- // would be statically compiled to slot indexes right after the superclass
- // ones, but we don't know the superclass statically. Instead, will
- // probably have to determine the field offset at class creation time in
- // the VM and then adjust by that every time a field is accessed/modified.
+ // Classes with no explicitly defined constructor get a default one.
+ int hasConstructor = 0;
+
+ // Compile the method definitions.
+ consume(compiler, TOKEN_LEFT_BRACE, "Expect '}' after class body.");
while (!match(compiler, TOKEN_RIGHT_BRACE))
{
@@ -1655,6 +1673,7 @@ void statement(Compiler* compiler)
{
// If the method name is prefixed with "this", it's a constructor.
instruction = CODE_METHOD_CTOR;
+ hasConstructor = 1;
}
SignatureFn signature = rules[compiler->parser->current.type].method;
@@ -1671,6 +1690,8 @@ void statement(Compiler* compiler)
"Expect newline after definition in class.");
}
+ if (!hasConstructor) defaultConstructor(compiler);
+
// Update the class with the number of fields.
compiler->fn->bytecode[numFieldsInstruction] = fields.count;
diff --git a/src/wren_vm.c b/src/wren_vm.c
index 70156c3..fed2f54 100644
--- a/src/wren_vm.c
+++ b/src/wren_vm.c
@@ -672,11 +672,9 @@ Value interpret(WrenVM* vm, Value function)
vm->objectClass = classObj;
}
- // Define a "new" method on the metaclass.
- // TODO(bob): Can this be inherited?
- int newSymbol = ensureSymbol(&vm->methods, "new", strlen("new"));
- classObj->metaclass->methods[newSymbol].type = METHOD_CTOR;
- classObj->metaclass->methods[newSymbol].fn = NULL_VAL;
+ // TODO(bob): If I remove this line, the method_call benchmark runs ~10%
+ // slower. WTF?
+ ensureSymbol(&vm->methods, "new", 3);
PUSH(OBJ_VAL(classObj));
DISPATCH();
@@ -915,19 +913,10 @@ Value interpret(WrenVM* vm, Value function)
// "this" in the body of the constructor and returned by it.
fiber->stack[fiber->stackSize - numArgs] = instance;
- if (IS_NULL(method->fn))
- {
- // Default constructor, so no body to call. Just discard the
- // stack slots for the arguments (but leave one for the instance).
- fiber->stackSize -= numArgs - 1;
- }
- else
- {
- // Invoke the constructor body.
- STORE_FRAME();
- wrenCallFunction(fiber, method->fn, numArgs);
- LOAD_FRAME();
- }
+ // Invoke the constructor body.
+ STORE_FRAME();
+ wrenCallFunction(fiber, method->fn, numArgs);
+ LOAD_FRAME();
break;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment