Skip to content

Commit 7c1ab1f

Browse files
author
John Messerly
committed
work around issue #51, super restrictions in V8
Adds an explicit "extends dart.Object", which we probably want for other reasons (e.g. toString, runtimeType, hashCode) Attempts to recover some readability by tweaking the name of the initialize functions. This closes #51, but we should open another tracking bug because there are concerning aspects to this workaround. [email protected], [email protected] Review URL: https://chromereviews.googleplex.com/150417015
1 parent 260cfc0 commit 7c1ab1f

File tree

17 files changed

+811
-804
lines changed

17 files changed

+811
-804
lines changed

pkg/dev_compiler/lib/runtime/dart_runtime.js

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -191,22 +191,34 @@ var dart;
191191
* superclass (prototype).
192192
*/
193193
function mixin(base/*, ...mixins*/) {
194-
// Build the mixin constructor. This runs the superclass as well as each
195-
// of the mixins' constructors.
196-
var mixins = Array.prototype.slice.call(arguments, 1);
197-
function Mixin() {
198-
base.apply(this, arguments);
199-
// Run mixin constructors. They cannot have arguments.
200-
for (var i = 0; i< mixins.length; i++) mixins[i].call(this);
201-
}
202194
// Inherit statics from Base to simulate ES6 class inheritance
203195
// Conceptually this is: `class Mixin extends base {}`
196+
function Mixin() {
197+
// TODO(jmesserly): since we're using initializers and not constructors,
198+
// we can just skip directly to DartObject.
199+
DartObject.apply(this, arguments);
200+
}
204201
Mixin.__proto__ = base;
205202
Mixin.prototype = Object.create(base.prototype);
203+
Mixin.prototype.constructor = Mixin;
206204
// Copy each mixin, with later ones overwriting earlier entries.
207-
for (var i = 0; i< mixins.length; i++) {
205+
var mixins = Array.prototype.slice.call(arguments, 1);
206+
for (var i = 0; i < mixins.length; i++) {
208207
copyProperties(Mixin.prototype, mixins[i].prototype);
209208
}
209+
// Create an initializer for the mixin, so when derived constructor calls
210+
// super, we can correctly initialize base and mixins.
211+
var baseCtor = base.prototype[base.name];
212+
Mixin.prototype[base.name] = function() {
213+
// Run mixin initializers. They cannot have arguments.
214+
// Run them backwards so most-derived mixin is initialized first.
215+
for (var i = mixins.length - 1; i >= 0; i--) {
216+
var mixin = mixins[i];
217+
mixin.prototype[mixin.name].call(this);
218+
}
219+
// Run base initializer.
220+
baseCtor.apply(this, arguments);
221+
}
210222
return Mixin;
211223
}
212224
dart.mixin = mixin;
@@ -256,7 +268,7 @@ var dart;
256268
*/
257269
function defineNamedConstructor(clazz, name) {
258270
var proto = clazz.prototype;
259-
var initMethod = proto[name];
271+
var initMethod = proto[clazz.name + '$' + name];
260272
var ctor = function() { return initMethod.apply(this, arguments); }
261273
ctor.prototype = proto;
262274
clazz[name] = ctor;
@@ -310,4 +322,26 @@ var dart;
310322
}
311323
dart.generic = generic;
312324

325+
326+
/**
327+
* Implements Dart constructor behavior. Because of V8 `super` [constructor
328+
* restrictions](https://code.google.com/p/v8/issues/detail?id=3330#c65) we
329+
* cannot currently emit actual ES6 constructors with super calls. Instead
330+
* we use the same trick as named constructors, and do them as instance
331+
* methods that perform initialization.
332+
*/
333+
// TODO(jmesserly): we'll need to rethink this once the ES6 spec and V8
334+
// settles. See <https://github.com/dart-lang/dart-dev-compiler/issues/51>.
335+
// Performance of this pattern is likely to be bad.
336+
dart.Object = function Object() {
337+
// Get the class name for this instance.
338+
var name = this.constructor.name;
339+
// Call the default constructor.
340+
var result = this[name].apply(this, arguments);
341+
return result === void 0 ? this : result;
342+
};
343+
// The initializer for dart.Object
344+
dart.Object.prototype.Object = function() {};
345+
dart.Object.prototype.constructor = dart.Object;
346+
313347
})(dart || (dart = {}));

pkg/dev_compiler/lib/src/codegen/js_codegen.dart

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -240,22 +240,20 @@ var $_libraryName;
240240
currentClass = node;
241241

242242
var name = node.name.name;
243-
244243
_beginTypeParameters(node.typeParameters, name);
245-
out.write('class $name');
244+
out.write('class $name extends ');
246245

247246
if (node.withClause != null) {
248-
out.write(' extends dart.mixin(');
249-
if (node.extendsClause != null) {
250-
_visitNode(node.extendsClause.superclass);
251-
} else {
252-
out.write('Object');
253-
}
247+
out.write('dart.mixin(');
248+
}
249+
if (node.extendsClause != null) {
250+
_visitNode(node.extendsClause.superclass);
251+
} else {
252+
out.write('dart.Object');
253+
}
254+
if (node.withClause != null) {
254255
_visitNodeList(node.withClause.mixinTypes, prefix: ', ', separator: ', ');
255256
out.write(')');
256-
} else if (node.extendsClause != null) {
257-
out.write(' extends ');
258-
_visitNode(node.extendsClause.superclass);
259257
}
260258

261259
out.write(' {\n', 2);
@@ -274,7 +272,7 @@ var $_libraryName;
274272
// Iff no constructor is specified for a class C, it implicitly has a
275273
// default constructor `C() : super() {}`, unless C is class Object.
276274
if (ctors.isEmpty && !node.element.type.isObject) {
277-
_generateImplicitConstructor(node, fields);
275+
_generateImplicitConstructor(node, name, fields);
278276
}
279277

280278
for (var member in node.members) {
@@ -344,12 +342,13 @@ $name.prototype[Symbol.iterator] = function() {
344342
/// Generates the implicit default constructor for class C of the form
345343
/// `C() : super() {}`.
346344
void _generateImplicitConstructor(
347-
ClassDeclaration node, List<FieldDeclaration> fields) {
348-
// If we don't have a method body, use the implicit JS ctor.
345+
ClassDeclaration node, String name, List<FieldDeclaration> fields) {
346+
// If we don't have a method body, skip this.
349347
if (fields.isEmpty) return;
350-
out.write('constructor() {\n', 2);
348+
349+
out.write('$name() {\n', 2);
351350
_initializeFields(fields);
352-
out.write('super();\n');
351+
_superConstructorCall(node);
353352
out.write('}\n', -2);
354353
}
355354

@@ -360,13 +359,14 @@ $name.prototype[Symbol.iterator] = function() {
360359
return;
361360
}
362361

362+
// We generate constructors as initializer methods in the class;
363+
// this allows use of `super` for instance methods/properties.
364+
// It also avoids V8 restrictions on `super` in default constructors.
365+
out.write(className);
363366
if (node.name != null) {
364-
// We generate named constructors as initializer methods in the class;
365-
// this allows use of `super` for instance methods/properties.
366-
out.write('/*constructor*/ ${node.name.name}(');
367-
} else {
368-
out.write('constructor(');
367+
out.write('\$${node.name.name}');
369368
}
369+
out.write('(');
370370
_visitNode(node.parameters);
371371
out.write(') {\n', 2);
372372
_generateConstructorBody(node, fields);
@@ -414,14 +414,11 @@ $name.prototype[Symbol.iterator] = function() {
414414
// If no superinitializer is provided, an implicit superinitializer of the
415415
// form `super()` is added at the end of the initializer list, unless the
416416
// enclosing class is class Object.
417-
ClassElement element = (node.parent as ClassDeclaration).element;
418417
if (superCall == null) {
419-
if (!element.type.isObject && !element.supertype.isObject) {
420-
_superConstructorCall(node);
421-
}
418+
_superConstructorCall(node.parent);
422419
} else {
423-
_superConstructorCall(
424-
node, superCall.constructorName, superCall.argumentList);
420+
_superConstructorCall(node.parent, node.name, superCall.constructorName,
421+
superCall.argumentList);
425422
}
426423
}
427424

@@ -450,22 +447,19 @@ $name.prototype[Symbol.iterator] = function() {
450447
out.write(');\n');
451448
}
452449

453-
void _superConstructorCall(ConstructorDeclaration ctor,
454-
[SimpleIdentifier superName, ArgumentList args]) {
455-
456-
// If we're calling default super from a named initializer method, we need
457-
// to do ES5 style `TypeName.call(this, <args>)`, otherwise we use `super`.
458-
if (ctor.name != null && superName == null) {
459-
_writeTypeName((ctor.parent as ClassDeclaration).element.supertype);
460-
out.write('.call(this');
461-
if (args != null && args.arguments.isNotEmpty) out.write(', ');
462-
_visitNode(args);
463-
} else {
464-
out.write('super');
465-
if (superName != null) out.write('.${superName.name}');
466-
out.write('(');
467-
_visitNode(args);
450+
void _superConstructorCall(ClassDeclaration clazz, [SimpleIdentifier ctorName,
451+
SimpleIdentifier superCtorName, ArgumentList args]) {
452+
var element = clazz.element;
453+
if (superCtorName == null &&
454+
(element.type.isObject || element.supertype.isObject)) {
455+
return;
468456
}
457+
458+
var supertypeName = element.supertype.name;
459+
out.write('super.$supertypeName');
460+
if (superCtorName != null) out.write('\$${superCtorName.name}');
461+
out.write('(');
462+
_visitNode(args);
469463
out.write(');\n');
470464
}
471465

pkg/dev_compiler/test/codegen/expect/BenchmarkBase/BenchmarkBase.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
var BenchmarkBase;
22
(function (BenchmarkBase) {
33
'use strict';
4-
class Expect {
4+
class Expect extends dart.Object {
55
static equals(expected, actual) {
66
if (!dart.equals(expected, actual)) {
77
throw `Values not equal: ${expected} vs ${actual}`;
@@ -20,8 +20,8 @@ var BenchmarkBase;
2020
}
2121
}
2222

23-
class BenchmarkBase {
24-
constructor(name) {
23+
class BenchmarkBase extends dart.Object {
24+
BenchmarkBase(name) {
2525
this.name = name;
2626
}
2727
run() {

pkg/dev_compiler/test/codegen/expect/DeltaBlue/DeltaBlue.js

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ var DeltaBlue;
77
}
88

99
class DeltaBlue extends BenchmarkBase.BenchmarkBase {
10-
constructor() {
11-
super("DeltaBlue");
10+
DeltaBlue() {
11+
super.BenchmarkBase("DeltaBlue");
1212
}
1313
run() {
1414
chainTest(100);
1515
projectionTest(100);
1616
}
1717
}
1818

19-
class Strength {
20-
constructor(value, name) {
19+
class Strength extends dart.Object {
20+
Strength(value, name) {
2121
this.value = value;
2222
this.name = name;
2323
}
@@ -43,8 +43,8 @@ var DeltaBlue;
4343
let NORMAL = new Strength(4, "normal");
4444
let WEAK_DEFAULT = new Strength(5, "weakDefault");
4545
let WEAKEST = new Strength(6, "weakest");
46-
class Constraint {
47-
constructor(strength) {
46+
class Constraint extends dart.Object {
47+
Constraint(strength) {
4848
this.strength = strength;
4949
}
5050
addConstraint() {
@@ -76,10 +76,10 @@ var DeltaBlue;
7676
}
7777

7878
class UnaryConstraint extends Constraint {
79-
constructor(myOutput, strength) {
79+
UnaryConstraint(myOutput, strength) {
8080
this.myOutput = myOutput;
8181
this.satisfied = false;
82-
super(strength);
82+
super.Constraint(strength);
8383
this.addConstraint();
8484
}
8585
addToGraph() {
@@ -109,16 +109,16 @@ var DeltaBlue;
109109
}
110110

111111
class StayConstraint extends UnaryConstraint {
112-
constructor(v, str) {
113-
super(v, str);
112+
StayConstraint(v, str) {
113+
super.UnaryConstraint(v, str);
114114
}
115115
execute() {
116116
}
117117
}
118118

119119
class EditConstraint extends UnaryConstraint {
120-
constructor(v, str) {
121-
super(v, str);
120+
EditConstraint(v, str) {
121+
super.UnaryConstraint(v, str);
122122
}
123123
isInput() { return true; }
124124
execute() {
@@ -129,11 +129,11 @@ var DeltaBlue;
129129
let FORWARD = 2;
130130
let BACKWARD = 0;
131131
class BinaryConstraint extends Constraint {
132-
constructor(v1, v2, strength) {
132+
BinaryConstraint(v1, v2, strength) {
133133
this.v1 = v1;
134134
this.v2 = v2;
135135
this.direction = NONE;
136-
super(strength);
136+
super.Constraint(strength);
137137
this.addConstraint();
138138
}
139139
chooseMethod(mark) {
@@ -181,10 +181,10 @@ var DeltaBlue;
181181
}
182182

183183
class ScaleConstraint extends BinaryConstraint {
184-
constructor(src, scale, offset, dest, strength) {
184+
ScaleConstraint(src, scale, offset, dest, strength) {
185185
this.scale = scale;
186186
this.offset = offset;
187-
super(src, dest, strength);
187+
super.BinaryConstraint(src, dest, strength);
188188
}
189189
addToGraph() {
190190
super.addToGraph();
@@ -216,16 +216,16 @@ var DeltaBlue;
216216
}
217217

218218
class EqualityConstraint extends BinaryConstraint {
219-
constructor(v1, v2, strength) {
220-
super(v1, v2, strength);
219+
EqualityConstraint(v1, v2, strength) {
220+
super.BinaryConstraint(v1, v2, strength);
221221
}
222222
execute() {
223223
this.output().value = this.input().value;
224224
}
225225
}
226226

227-
class Variable {
228-
constructor(name, value) {
227+
class Variable extends dart.Object {
228+
Variable(name, value) {
229229
this.constraints = new List.from([]);
230230
this.name = name;
231231
this.value = value;
@@ -243,10 +243,9 @@ var DeltaBlue;
243243
}
244244
}
245245

246-
class Planner {
247-
constructor() {
246+
class Planner extends dart.Object {
247+
Planner() {
248248
this.currentMark = 0;
249-
super();
250249
}
251250
incrementalAdd(c) {
252251
let mark = this.newMark();
@@ -335,10 +334,9 @@ var DeltaBlue;
335334
}
336335
}
337336

338-
class Plan {
339-
constructor() {
337+
class Plan extends dart.Object {
338+
Plan() {
340339
this.list = new List.from([]);
341-
super();
342340
}
343341
addConstraint(c) {
344342
this.list.add(c);

0 commit comments

Comments
 (0)