From 41754310080ee63a7ee31ebd60faaaf621d1314b Mon Sep 17 00:00:00 2001 From: "floitsch@google.com" Date: Mon, 7 May 2012 13:40:41 +0000 Subject: [PATCH] Create the JavaScript constructor dynamically. Review URL: https://chromiumcodereview.appspot.com//10344021 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@7376 260f80e4-7a28-3924-810f-c04153c831b5 --- lib/compiler/implementation/emitter.dart | 73 ++++++++++++------------ tests/language/class_codegen_test.dart | 28 +++++++++ 2 files changed, 66 insertions(+), 35 deletions(-) create mode 100644 tests/language/class_codegen_test.dart diff --git a/lib/compiler/implementation/emitter.dart b/lib/compiler/implementation/emitter.dart index 28e33ed94f4..8cf005060cd 100644 --- a/lib/compiler/implementation/emitter.dart +++ b/lib/compiler/implementation/emitter.dart @@ -53,11 +53,12 @@ class CodeEmitterTask extends CompilerTask { => '${namer.ISOLATE}.${namer.ISOLATE_PROPERTIES}'; String get defineClassFunction() { + // First the class name, then the super class name, followed by the fields + // (in an array) and the members (inside an Object literal). + // The caller can also pass in the constructor as a function if needed. + // // Example: - // defineClass("A", "B", - // function(x) { /* The JavaScript constructor. */ - // this.x = x; - // }, { /* The members inside an Object literal. */ + // defineClass("A", "B", ["x", "y"], { // foo$1: function(y) { // print(this.x + y); // }, @@ -66,7 +67,21 @@ class CodeEmitterTask extends CompilerTask { // }, // }); return """ -function(cls, superclass, constructor, prototype) { +function(cls, superclass, fields, prototype) { + var constructor; + if (typeof fields == 'function') { + constructor = fields; + } else { + var str = "(function " + cls + "("; + var body = ""; + for (var i = 0; i < fields.length; i++) { + if (i != 0) str += ", "; + str += fields[i]; + body += "this." + fields[i] + " = " + fields[i] + ";\\n"; + } + str += ") {" + body + "})"; + constructor = eval(str); + } $isolatePropertiesName[cls] = constructor; constructor.prototype = prototype; if (superclass !== "") { @@ -359,30 +374,16 @@ function() { emitExtraAccessors(member, defineInstanceMember); } - bool generateFieldInits(ClassElement classElement, - StringBuffer argumentsBuffer, - StringBuffer bodyBuffer) { - bool isFirst = true; - void generateFieldInit(ClassElement enclosingClass, Element member) { - // TODO(floitsch): make sure there are no name clashes. - String className = namer.getName(enclosingClass); - if (!isFirst) argumentsBuffer.add(', '); - isFirst = false; - String memberName = namer.instanceFieldName(member.getLibrary(), - member.name); - String parameter; - if (classElement === enclosingClass) { - parameter = memberName; - } else { - parameter = '${className}_$memberName'; - } - argumentsBuffer.add(parameter); - bodyBuffer.add(' this.$memberName = $parameter;\n'); + List generateFieldList(ClassElement classElement) { + List result = []; + void addField(ClassElement enclosingClass, Element member) { + result.add(namer.instanceFieldName(member.getLibrary(), member.name)); } - classElement.forEachInstanceField(generateFieldInit, + classElement.forEachInstanceField(addField, includeBackendMembers: true, includeSuperMembers: true); + return result; } void generateClass(ClassElement classElement, StringBuffer buffer) { @@ -406,18 +407,20 @@ function() { } String constructorName = namer.safeName(classElement.name.slowToString()); buffer.add('$defineClassName("$className", "$superName", '); - buffer.add('function $constructorName('); - StringBuffer bodyBuffer = new StringBuffer(); // If the class is never instantiated we still need to set it up for - // inheritance purposes, but we can leave its JavaScript constructor empty. - if (compiler.universe.instantiatedClasses.contains(classElement)) { - generateFieldInits(classElement, buffer, bodyBuffer); + // inheritance purposes, but we can simplify its JavaScript constructor. + if (!compiler.universe.instantiatedClasses.contains(classElement)) { + buffer.add("[]"); + } else { + List fields = generateFieldList(classElement); + buffer.add('['); + for (int i = 0; i < fields.length; i++) { + if (i != 0) buffer.add(", "); + buffer.add('"${fields[i]}"'); + } + buffer.add(']'); } - buffer.add(') {\n'); - buffer.add(bodyBuffer); - buffer.add(' }, '); - - buffer.add('{\n'); + buffer.add(', {\n'); void defineInstanceMember(String name, String value) { buffer.add(' $name: $value,\n'); diff --git a/tests/language/class_codegen_test.dart b/tests/language/class_codegen_test.dart new file mode 100644 index 00000000000..1b1c738c358 --- /dev/null +++ b/tests/language/class_codegen_test.dart @@ -0,0 +1,28 @@ +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class A { + A() : x = 3; + foo() => x; + var x; +} + +class B extends A { + bar() => 499; +} + +class C extends A { + bar() => 42; +} + +main() { + // We don't instantiate A, but the codegen still needs to emit (parts of) it + // for inheritence purposes. + var b = new B(); + var c = new C(); + Expect.equals(3, b.foo()); + Expect.equals(3, c.foo()); + Expect.equals(499, b.bar()); + Expect.equals(42, c.bar()); +}