Index: lib/src/compiler/code_generator.dart |
diff --git a/lib/src/compiler/code_generator.dart b/lib/src/compiler/code_generator.dart |
index 134907c8e4f6cf74d67e02455d7430e00961a8ed..95dba1cdabaa92703a3e0c6a11ea8fa81638d0fa 100644 |
--- a/lib/src/compiler/code_generator.dart |
+++ b/lib/src/compiler/code_generator.dart |
@@ -224,12 +224,21 @@ class CodeGenerator extends GeneralizingAstVisitor |
// Collect all Element -> Node mappings, in case we need to forward declare |
// any nodes. |
var nodes = new HashMap<Element, AstNode>.identity(); |
- compilationUnits.map(_collectElements).forEach(nodes.addAll); |
+ var sdkBootstrappingFns = new List<FunctionElement>(); |
+ for (var unit in compilationUnits) { |
+ if (_isDartRuntime(unit.element.library)) { |
+ sdkBootstrappingFns.addAll(unit.element.functions); |
+ } |
+ _collectElements(unit, nodes); |
+ } |
_loader = new ElementLoader(_emitModuleItem, nodes); |
// Add implicit dart:core dependency so it is first. |
emitLibraryName(dartCoreLibrary); |
+ // Emit SDK bootstrapping functions first, if any. |
+ sdkBootstrappingFns.forEach(_loader.emitDeclaration); |
+ |
// Visit each compilation unit and emit its code. |
// |
// NOTE: declarations are not necessarily emitted in this order. |
@@ -319,8 +328,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
/// Collect toplevel elements and nodes we need to emit, and returns |
/// an ordered map of these. |
- static Map<Element, AstNode> _collectElements(CompilationUnit unit) { |
- var map = <Element, AstNode>{}; |
+ static void _collectElements( |
+ CompilationUnit unit, Map<Element, AstNode> map) { |
for (var declaration in unit.declarations) { |
if (declaration is TopLevelVariableDeclaration) { |
for (var field in declaration.variables.variables) { |
@@ -330,7 +339,6 @@ class CodeGenerator extends GeneralizingAstVisitor |
map[declaration.element] = declaration; |
} |
} |
- return map; |
} |
void _emitModuleItem(AstNode node) { |
@@ -961,7 +969,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
// Generate a corresponding virtual getter / setter. |
var name = _elementMemberName(methodElement, |
- allowExtensions: _extensionTypes.isNativeClass(type.element)); |
+ useExtension: _extensionTypes.isNativeClass(type.element)); |
if (method.isGetter) { |
// Generate a setter |
if (field.setter != null || !propertyOverrideResult.foundSetter) |
@@ -1116,7 +1124,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
if (extensions.isNotEmpty) { |
var methodNames = <JS.Expression>[]; |
for (var e in extensions) { |
- methodNames.add(_elementMemberName(e, allowExtensions: false)); |
+ methodNames.add(_elementMemberName(e, useExtension: false)); |
} |
body.add(js.statement('dart.defineExtensionMembers(#, #);', [ |
className, |
@@ -1154,7 +1162,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
continue; |
} |
var memberName = _elementMemberName(element, |
- allowExtensions: _extensionTypes.isNativeClass(classElem)); |
+ useExtension: _extensionTypes.isNativeClass(classElem)); |
var parts = _emitFunctionTypeParts(element.type); |
var property = |
new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
@@ -1210,7 +1218,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
var dartxNames = <JS.Expression>[]; |
for (var m in methods) { |
if (!m.isAbstract && !m.isStatic && m.element.isPublic) { |
- dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); |
+ dartxNames.add(_elementMemberName(m.element, useExtension: false)); |
} |
} |
for (var f in fields) { |
@@ -1218,7 +1226,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
for (var d in f.fields.variables) { |
if (d.element.isPublic) { |
dartxNames.add( |
- _elementMemberName(d.element.getter, allowExtensions: false)); |
+ _elementMemberName(d.element.getter, useExtension: false)); |
} |
} |
} |
@@ -1243,7 +1251,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
var extensionMembers = new HashSet<String>(); |
for (var t in types) { |
for (var m in [t.methods, t.accessors].expand((e) => e)) { |
- if (!m.isStatic) extensionMembers.add(m.name); |
+ if (!m.isStatic && m.isPublic) extensionMembers.add(m.name); |
} |
} |
@@ -1677,7 +1685,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
return annotate( |
new JS.Method( |
_elementMemberName(node.element, |
- allowExtensions: _extensionTypes.isNativeClass(type.element)), |
+ useExtension: _extensionTypes.isNativeClass(type.element)), |
fn, |
isGetter: node.isGetter, |
isSetter: node.isSetter, |
@@ -2492,9 +2500,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
} |
if (_isObjectMemberCall(target, name)) { |
- // Object methods require a helper for null checks & native types. |
assert(typeArgs == null); // Object methods don't take type args. |
- return js.call('dart.#(#, #)', [memberName, jsTarget, args]); |
+ return js.call('dart.#(#, #)', [name, jsTarget, args]); |
} |
jsTarget = new JS.PropertyAccess(jsTarget, memberName); |
@@ -2845,11 +2852,6 @@ class CodeGenerator extends GeneralizingAstVisitor |
return new JS.VariableInitialization(name, _visitInitializer(node)); |
} |
- bool _isFinalJSDecl(AstNode field) => |
- field is VariableDeclaration && |
- field.isFinal && |
- _isJSInvocation(field.initializer); |
- |
/// Try to emit a constant static field. |
/// |
/// If the field's initializer does not cause side effects, and if all of |
@@ -2915,10 +2917,9 @@ class CodeGenerator extends GeneralizingAstVisitor |
eagerInit = false; |
} |
- // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile |
- // runtime helpers. |
- // TODO(jmesserly): we don't track dependencies correctly for these. |
- var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); |
+ // Treat dart:runtime stuff as safe to eagerly evaluate. |
+ // TODO(jmesserly): it'd be nice to avoid this special case. |
+ var isJSTopLevel = field.isFinal && _isDartRuntime(element.library); |
if (eagerInit || isJSTopLevel) { |
// Remember that we emitted it this way, so re-export can take advantage |
// of this fact. |
@@ -3611,8 +3612,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
/// For example `null.toString()` is legal in Dart, so we need to generate |
/// that as `dart.toString(obj)`. |
bool _isObjectMemberCall(Expression target, String memberName) { |
- // Is this a member on `Object`? |
- if (!isObjectProperty(memberName)) { |
+ if (!isObjectMember(memberName)) { |
return false; |
} |
@@ -3690,12 +3690,13 @@ class CodeGenerator extends GeneralizingAstVisitor |
if (isSuper) { |
result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]); |
} else if (_isObjectMemberCall(target, memberName)) { |
- result = js.call('dart.bind(#, #, dart.#)', [jsTarget, name, name]); |
+ result = js.call('dart.bind(#, #, dart.#)', |
+ [jsTarget, _propertyName(memberName), memberName]); |
} else { |
result = js.call('dart.bind(#, #)', [jsTarget, name]); |
} |
} else if (_isObjectMemberCall(target, memberName)) { |
- result = js.call('dart.#(#)', [name, jsTarget]); |
+ result = js.call('dart.#(#)', [memberName, jsTarget]); |
} else { |
result = js.call('#.#', [jsTarget, name]); |
} |
@@ -4161,8 +4162,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
/// |
/// Unlike call sites, we always have an element available, so we can use it |
/// directly rather than computing the relevant options for [_emitMemberName]. |
- JS.Expression _elementMemberName(ExecutableElement e, |
- {bool allowExtensions: true}) { |
+ JS.Expression _elementMemberName(ExecutableElement e, {bool useExtension}) { |
String name; |
if (e is PropertyAccessorElement) { |
name = e.variable.name; |
@@ -4173,7 +4173,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
type: (e.enclosingElement as ClassElement).type, |
unary: e.parameters.isEmpty, |
isStatic: e.isStatic, |
- allowExtensions: allowExtensions); |
+ useExtension: useExtension); |
} |
/// This handles member renaming for private names and operators. |
@@ -4221,7 +4221,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
{DartType type, |
bool unary: false, |
bool isStatic: false, |
- bool allowExtensions: true}) { |
+ bool useExtension}) { |
// Static members skip the rename steps. |
if (isStatic) return _propertyName(name); |
@@ -4241,19 +4241,20 @@ class CodeGenerator extends GeneralizingAstVisitor |
name = '+$name'; |
} |
- // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
- var baseType = type; |
- while (baseType is TypeParameterType) { |
- baseType = baseType.element.bound; |
- } |
- if (allowExtensions && |
- baseType != null && |
- _extensionTypes.hasNativeSubtype(baseType) && |
- !isObjectProperty(name)) { |
- return js.call('dartx.#', _propertyName(name)); |
+ var result = _propertyName(name); |
+ |
+ if (useExtension == null) { |
+ // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
+ var baseType = type; |
+ while (baseType is TypeParameterType) { |
+ baseType = baseType.element.bound; |
+ } |
+ useExtension = baseType != null && |
+ _extensionTypes.hasNativeSubtype(baseType) && |
+ !isObjectMember(name); |
} |
- return _propertyName(name); |
+ return useExtension ? js.call('dartx.#', result) : result; |
} |
JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { |
@@ -4299,18 +4300,22 @@ class CodeGenerator extends GeneralizingAstVisitor |
/// a subtype of int and double (and num). It's in our "dart:_interceptors". |
bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType); |
- bool _isObjectGetter(String name) { |
- PropertyAccessorElement element = types.objectType.element.getGetter(name); |
- return (element != null && !element.isStatic); |
- } |
- |
- bool _isObjectMethod(String name) { |
- MethodElement element = types.objectType.element.getMethod(name); |
- return (element != null && !element.isStatic); |
- } |
- |
- bool isObjectProperty(String name) { |
- return _isObjectGetter(name) || _isObjectMethod(name); |
+ /// Return true if this is one of the methods/properties on all Dart Objects |
+ /// (toString, hashCode, noSuchMethod, runtimeType). |
+ /// |
+ /// Operator == is excluded, as it is handled as part of the equality binary |
+ /// operator. |
+ bool isObjectMember(String name) { |
+ // We could look these up on Object, but we have hard coded runtime helpers |
+ // so it's not really providing any benefit. |
+ switch (name) { |
+ case 'hashCode': |
+ case 'toString': |
+ case 'noSuchMethod': |
+ case 'runtimeType': |
+ return true; |
+ } |
+ return false; |
} |
// TODO(leafp): Various analyzer pieces computed similar things. |