Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:collection' show HashMap, HashSet; | 5 import 'dart:collection' show HashMap, HashSet; |
| 6 import 'dart:math' show min, max; | 6 import 'dart:math' show min, max; |
| 7 | 7 |
| 8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
| (...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 // TODO(jmesserly): find a cleaner design for this. | 217 // TODO(jmesserly): find a cleaner design for this. |
| 218 if (_isDartRuntime(library)) { | 218 if (_isDartRuntime(library)) { |
| 219 items.add(new JS.ExportDeclaration( | 219 items.add(new JS.ExportDeclaration( |
| 220 js.call('const # = Object.create(null)', [_dartxVar]))); | 220 js.call('const # = Object.create(null)', [_dartxVar]))); |
| 221 } | 221 } |
| 222 } | 222 } |
| 223 | 223 |
| 224 // Collect all Element -> Node mappings, in case we need to forward declare | 224 // Collect all Element -> Node mappings, in case we need to forward declare |
| 225 // any nodes. | 225 // any nodes. |
| 226 var nodes = new HashMap<Element, AstNode>.identity(); | 226 var nodes = new HashMap<Element, AstNode>.identity(); |
| 227 compilationUnits.map(_collectElements).forEach(nodes.addAll); | 227 var sdkBootstrappingFns = new List<FunctionElement>(); |
| 228 for (var unit in compilationUnits) { | |
| 229 _collectElements(unit, nodes, sdkBootstrappingFns); | |
| 230 } | |
| 228 _loader = new ElementLoader(_emitModuleItem, nodes); | 231 _loader = new ElementLoader(_emitModuleItem, nodes); |
| 229 | 232 |
| 230 // Add implicit dart:core dependency so it is first. | 233 // Add implicit dart:core dependency so it is first. |
| 231 emitLibraryName(dartCoreLibrary); | 234 emitLibraryName(dartCoreLibrary); |
| 232 | 235 |
| 236 // Emit SDK bootstrapping functions first, if any. | |
| 237 sdkBootstrappingFns.forEach(_loader.emitDeclaration); | |
| 238 | |
| 233 // Visit each compilation unit and emit its code. | 239 // Visit each compilation unit and emit its code. |
| 234 // | 240 // |
| 235 // NOTE: declarations are not necessarily emitted in this order. | 241 // NOTE: declarations are not necessarily emitted in this order. |
| 236 // Order will be changed as needed so the resulting code can execute. | 242 // Order will be changed as needed so the resulting code can execute. |
| 237 // This is done by forward declaring items. | 243 // This is done by forward declaring items. |
| 238 compilationUnits.forEach(visitCompilationUnit); | 244 compilationUnits.forEach(visitCompilationUnit); |
| 239 | 245 |
| 240 // Declare imports | 246 // Declare imports |
| 241 _finishImports(items); | 247 _finishImports(items); |
| 242 | 248 |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 312 imports.add(new JS.NameSpecifier(_runtimeLibVar)); | 318 imports.add(new JS.NameSpecifier(_runtimeLibVar)); |
| 313 imports.add(new JS.NameSpecifier(_dartxVar)); | 319 imports.add(new JS.NameSpecifier(_dartxVar)); |
| 314 } | 320 } |
| 315 items.add(new JS.ImportDeclaration( | 321 items.add(new JS.ImportDeclaration( |
| 316 namedImports: imports, from: js.string(module, "'"))); | 322 namedImports: imports, from: js.string(module, "'"))); |
| 317 }); | 323 }); |
| 318 } | 324 } |
| 319 | 325 |
| 320 /// Collect toplevel elements and nodes we need to emit, and returns | 326 /// Collect toplevel elements and nodes we need to emit, and returns |
| 321 /// an ordered map of these. | 327 /// an ordered map of these. |
| 322 static Map<Element, AstNode> _collectElements(CompilationUnit unit) { | 328 static void _collectElements(CompilationUnit unit, Map<Element, AstNode> map, |
| 323 var map = <Element, AstNode>{}; | 329 List<FunctionElement> runtimeHelpers) { |
| 330 var library = unit.element.library; | |
| 331 if (_isDartRuntime(library)) { | |
| 332 runtimeHelpers.addAll(unit.element.functions); | |
| 333 } | |
| 334 | |
| 324 for (var declaration in unit.declarations) { | 335 for (var declaration in unit.declarations) { |
| 325 if (declaration is TopLevelVariableDeclaration) { | 336 if (declaration is TopLevelVariableDeclaration) { |
| 326 for (var field in declaration.variables.variables) { | 337 for (var field in declaration.variables.variables) { |
| 327 map[field.element] = field; | 338 map[field.element] = field; |
| 328 } | 339 } |
| 329 } else { | 340 } else { |
|
vsm
2016/05/09 20:55:56
Is it worth skipping items already in runtimeHelpe
Jennifer Messerly
2016/05/10 00:33:57
Hmmm. We actually can't as we need the Element ->
| |
| 330 map[declaration.element] = declaration; | 341 map[declaration.element] = declaration; |
| 331 } | 342 } |
| 332 } | 343 } |
| 333 return map; | |
| 334 } | 344 } |
| 335 | 345 |
| 336 void _emitModuleItem(AstNode node) { | 346 void _emitModuleItem(AstNode node) { |
| 337 // TODO(jmesserly): ideally we could do this at a smaller granularity. | 347 // TODO(jmesserly): ideally we could do this at a smaller granularity. |
| 338 // We'll need to be consistent about when we're generating functions, and | 348 // We'll need to be consistent about when we're generating functions, and |
| 339 // only run this on the outermost function. | 349 // only run this on the outermost function. |
| 340 inferNullableTypes(node); | 350 inferNullableTypes(node); |
| 341 | 351 |
| 342 var code = _visit(node); | 352 var code = _visit(node); |
| 343 if (code != null) _moduleItems.add(code); | 353 if (code != null) _moduleItems.add(code); |
| (...skipping 610 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 954 JS.Method _emitSuperAccessorWrapper(MethodDeclaration method, | 964 JS.Method _emitSuperAccessorWrapper(MethodDeclaration method, |
| 955 InterfaceType type, List<ClassElement> superclasses) { | 965 InterfaceType type, List<ClassElement> superclasses) { |
| 956 var methodElement = method.element as PropertyAccessorElement; | 966 var methodElement = method.element as PropertyAccessorElement; |
| 957 var field = methodElement.variable; | 967 var field = methodElement.variable; |
| 958 if (!field.isSynthetic) return null; | 968 if (!field.isSynthetic) return null; |
| 959 var propertyOverrideResult = checkForPropertyOverride( | 969 var propertyOverrideResult = checkForPropertyOverride( |
| 960 methodElement.variable, superclasses, _extensionTypes); | 970 methodElement.variable, superclasses, _extensionTypes); |
| 961 | 971 |
| 962 // Generate a corresponding virtual getter / setter. | 972 // Generate a corresponding virtual getter / setter. |
| 963 var name = _elementMemberName(methodElement, | 973 var name = _elementMemberName(methodElement, |
| 964 allowExtensions: _extensionTypes.isNativeClass(type.element)); | 974 useExtension: _extensionTypes.isNativeClass(type.element)); |
| 965 if (method.isGetter) { | 975 if (method.isGetter) { |
| 966 // Generate a setter | 976 // Generate a setter |
| 967 if (field.setter != null || !propertyOverrideResult.foundSetter) | 977 if (field.setter != null || !propertyOverrideResult.foundSetter) |
| 968 return null; | 978 return null; |
| 969 var fn = js.call('function(value) { super[#] = value; }', [name]); | 979 var fn = js.call('function(value) { super[#] = value; }', [name]); |
| 970 return new JS.Method(name, fn, isSetter: true); | 980 return new JS.Method(name, fn, isSetter: true); |
| 971 } else { | 981 } else { |
| 972 // Generate a getter | 982 // Generate a getter |
| 973 if (field.getter != null || !propertyOverrideResult.foundGetter) | 983 if (field.getter != null || !propertyOverrideResult.foundGetter) |
| 974 return null; | 984 return null; |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1108 | 1118 |
| 1109 /// If a concrete class implements one of our extensions, we might need to | 1119 /// If a concrete class implements one of our extensions, we might need to |
| 1110 /// add forwarders. | 1120 /// add forwarders. |
| 1111 void _defineExtensionMembers(List<ExecutableElement> extensions, | 1121 void _defineExtensionMembers(List<ExecutableElement> extensions, |
| 1112 JS.Expression className, List<JS.Statement> body) { | 1122 JS.Expression className, List<JS.Statement> body) { |
| 1113 // If a concrete class implements one of our extensions, we might need to | 1123 // If a concrete class implements one of our extensions, we might need to |
| 1114 // add forwarders. | 1124 // add forwarders. |
| 1115 if (extensions.isNotEmpty) { | 1125 if (extensions.isNotEmpty) { |
| 1116 var methodNames = <JS.Expression>[]; | 1126 var methodNames = <JS.Expression>[]; |
| 1117 for (var e in extensions) { | 1127 for (var e in extensions) { |
| 1118 methodNames.add(_elementMemberName(e, allowExtensions: false)); | 1128 methodNames.add(_elementMemberName(e, useExtension: false)); |
| 1119 } | 1129 } |
| 1120 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ | 1130 body.add(js.statement('dart.defineExtensionMembers(#, #);', [ |
| 1121 className, | 1131 className, |
| 1122 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) | 1132 new JS.ArrayInitializer(methodNames, multiline: methodNames.length > 4) |
| 1123 ])); | 1133 ])); |
| 1124 } | 1134 } |
| 1125 } | 1135 } |
| 1126 | 1136 |
| 1127 /// Emit the signature on the class recording the runtime type information | 1137 /// Emit the signature on the class recording the runtime type information |
| 1128 void _emitClassSignature( | 1138 void _emitClassSignature( |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 1146 for (MethodDeclaration node in methods) { | 1156 for (MethodDeclaration node in methods) { |
| 1147 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 1157 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
| 1148 var name = node.name.name; | 1158 var name = node.name.name; |
| 1149 var element = node.element; | 1159 var element = node.element; |
| 1150 var inheritedElement = | 1160 var inheritedElement = |
| 1151 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 1161 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
| 1152 if (inheritedElement != null && inheritedElement.type == element.type) { | 1162 if (inheritedElement != null && inheritedElement.type == element.type) { |
| 1153 continue; | 1163 continue; |
| 1154 } | 1164 } |
| 1155 var memberName = _elementMemberName(element, | 1165 var memberName = _elementMemberName(element, |
| 1156 allowExtensions: _extensionTypes.isNativeClass(classElem)); | 1166 useExtension: _extensionTypes.isNativeClass(classElem)); |
| 1157 var parts = _emitFunctionTypeParts(element.type); | 1167 var parts = _emitFunctionTypeParts(element.type); |
| 1158 var property = | 1168 var property = |
| 1159 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 1169 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| 1160 if (node.isStatic) { | 1170 if (node.isStatic) { |
| 1161 tStatics.add(property); | 1171 tStatics.add(property); |
| 1162 sNames.add(memberName); | 1172 sNames.add(memberName); |
| 1163 } else { | 1173 } else { |
| 1164 tMethods.add(property); | 1174 tMethods.add(property); |
| 1165 } | 1175 } |
| 1166 } | 1176 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1202 /// Ensure `dartx.` symbols we will use are present. | 1212 /// Ensure `dartx.` symbols we will use are present. |
| 1203 void _initExtensionSymbols( | 1213 void _initExtensionSymbols( |
| 1204 ClassElement classElem, | 1214 ClassElement classElem, |
| 1205 List<MethodDeclaration> methods, | 1215 List<MethodDeclaration> methods, |
| 1206 List<FieldDeclaration> fields, | 1216 List<FieldDeclaration> fields, |
| 1207 List<JS.Statement> body) { | 1217 List<JS.Statement> body) { |
| 1208 if (_extensionTypes.hasNativeSubtype(classElem.type)) { | 1218 if (_extensionTypes.hasNativeSubtype(classElem.type)) { |
| 1209 var dartxNames = <JS.Expression>[]; | 1219 var dartxNames = <JS.Expression>[]; |
| 1210 for (var m in methods) { | 1220 for (var m in methods) { |
| 1211 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { | 1221 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { |
| 1212 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); | 1222 dartxNames.add(_elementMemberName(m.element, useExtension: false)); |
| 1213 } | 1223 } |
| 1214 } | 1224 } |
| 1215 for (var f in fields) { | 1225 for (var f in fields) { |
| 1216 if (!f.isStatic) { | 1226 if (!f.isStatic) { |
| 1217 for (var d in f.fields.variables) { | 1227 for (var d in f.fields.variables) { |
| 1218 if (d.element.isPublic) { | 1228 if (d.element.isPublic) { |
| 1219 dartxNames.add( | 1229 dartxNames.add( |
| 1220 _elementMemberName(d.element.getter, allowExtensions: false)); | 1230 _elementMemberName(d.element.getter, useExtension: false)); |
| 1221 } | 1231 } |
| 1222 } | 1232 } |
| 1223 } | 1233 } |
| 1224 } | 1234 } |
| 1225 if (dartxNames.isNotEmpty) { | 1235 if (dartxNames.isNotEmpty) { |
| 1226 body.add(js.statement('dart.defineExtensionNames(#)', | 1236 body.add(js.statement('dart.defineExtensionNames(#)', |
| 1227 [new JS.ArrayInitializer(dartxNames, multiline: true)])); | 1237 [new JS.ArrayInitializer(dartxNames, multiline: true)])); |
| 1228 } | 1238 } |
| 1229 } | 1239 } |
| 1230 } | 1240 } |
| 1231 | 1241 |
| 1232 List<ExecutableElement> _extensionsToImplement(ClassElement element) { | 1242 List<ExecutableElement> _extensionsToImplement(ClassElement element) { |
| 1233 var members = <ExecutableElement>[]; | 1243 var members = <ExecutableElement>[]; |
| 1234 if (_extensionTypes.isNativeClass(element)) return members; | 1244 if (_extensionTypes.isNativeClass(element)) return members; |
| 1235 | 1245 |
| 1236 // Collect all extension types we implement. | 1246 // Collect all extension types we implement. |
| 1237 var type = element.type; | 1247 var type = element.type; |
| 1238 var types = _extensionTypes.collectNativeInterfaces(element); | 1248 var types = _extensionTypes.collectNativeInterfaces(element); |
| 1239 if (types.isEmpty) return members; | 1249 if (types.isEmpty) return members; |
| 1240 | 1250 |
| 1241 // Collect all possible extension method names. | 1251 // Collect all possible extension method names. |
| 1242 var extensionMembers = new HashSet<String>(); | 1252 var extensionMembers = new HashSet<String>(); |
| 1243 for (var t in types) { | 1253 for (var t in types) { |
| 1244 for (var m in [t.methods, t.accessors].expand((e) => e)) { | 1254 for (var m in [t.methods, t.accessors].expand((e) => e)) { |
| 1245 if (!m.isStatic) extensionMembers.add(m.name); | 1255 if (!m.isStatic && m.isPublic) extensionMembers.add(m.name); |
|
vsm
2016/05/09 20:55:56
Do we still need Bob's fix here:
https://coderevi
Jennifer Messerly
2016/05/10 00:33:57
Well, Bob's test is still passing so that's a good
| |
| 1246 } | 1256 } |
| 1247 } | 1257 } |
| 1248 | 1258 |
| 1249 // Collect all of extension methods this type implements. | 1259 // Collect all of extension methods this type implements. |
| 1250 for (var m in [type.methods, type.accessors].expand((e) => e)) { | 1260 for (var m in [type.methods, type.accessors].expand((e) => e)) { |
| 1251 if (!m.isStatic && !m.isAbstract && extensionMembers.contains(m.name)) { | 1261 if (!m.isStatic && !m.isAbstract && extensionMembers.contains(m.name)) { |
| 1252 members.add(m); | 1262 members.add(m); |
| 1253 } | 1263 } |
| 1254 } | 1264 } |
| 1255 return members; | 1265 return members; |
| (...skipping 413 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1669 // call sites, but it's cleaner to instead transform the operator method . | 1679 // call sites, but it's cleaner to instead transform the operator method . |
| 1670 fn = _alwaysReturnLastParameter(fn); | 1680 fn = _alwaysReturnLastParameter(fn); |
| 1671 } | 1681 } |
| 1672 | 1682 |
| 1673 fn = _makeGenericFunction(fn); | 1683 fn = _makeGenericFunction(fn); |
| 1674 } | 1684 } |
| 1675 | 1685 |
| 1676 return annotate( | 1686 return annotate( |
| 1677 new JS.Method( | 1687 new JS.Method( |
| 1678 _elementMemberName(node.element, | 1688 _elementMemberName(node.element, |
| 1679 allowExtensions: _extensionTypes.isNativeClass(type.element)), | 1689 useExtension: _extensionTypes.isNativeClass(type.element)), |
| 1680 fn, | 1690 fn, |
| 1681 isGetter: node.isGetter, | 1691 isGetter: node.isGetter, |
| 1682 isSetter: node.isSetter, | 1692 isSetter: node.isSetter, |
| 1683 isStatic: node.isStatic), | 1693 isStatic: node.isStatic), |
| 1684 node, | 1694 node, |
| 1685 node.element); | 1695 node.element); |
| 1686 } | 1696 } |
| 1687 | 1697 |
| 1688 /// Transform the function so the last parameter is always returned. | 1698 /// Transform the function so the last parameter is always returned. |
| 1689 /// | 1699 /// |
| (...skipping 782 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2472 JS.Expression jsTarget = _visit(target); | 2482 JS.Expression jsTarget = _visit(target); |
| 2473 if (DynamicInvoke.get(target)) { | 2483 if (DynamicInvoke.get(target)) { |
| 2474 if (typeArgs != null) { | 2484 if (typeArgs != null) { |
| 2475 return js.call('dart.dgsend(#, #, #, #)', | 2485 return js.call('dart.dgsend(#, #, #, #)', |
| 2476 [jsTarget, new JS.ArrayInitializer(typeArgs), memberName, args]); | 2486 [jsTarget, new JS.ArrayInitializer(typeArgs), memberName, args]); |
| 2477 } else { | 2487 } else { |
| 2478 return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); | 2488 return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); |
| 2479 } | 2489 } |
| 2480 } | 2490 } |
| 2481 if (_isObjectMemberCall(target, name)) { | 2491 if (_isObjectMemberCall(target, name)) { |
| 2482 // Object methods require a helper for null checks & native types. | |
| 2483 assert(typeArgs == null); // Object methods don't take type args. | 2492 assert(typeArgs == null); // Object methods don't take type args. |
| 2484 return js.call('dart.#(#, #)', [memberName, jsTarget, args]); | 2493 return js.call('dart.#(#, #)', [name, jsTarget, args]); |
| 2485 } | 2494 } |
| 2486 | 2495 |
| 2487 jsTarget = new JS.PropertyAccess(jsTarget, memberName); | 2496 jsTarget = new JS.PropertyAccess(jsTarget, memberName); |
| 2488 | 2497 |
| 2489 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); | 2498 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
| 2490 | 2499 |
| 2491 if (DynamicInvoke.get(node.methodName)) { | 2500 if (DynamicInvoke.get(node.methodName)) { |
| 2492 // This is a dynamic call to a statically known target. For example: | 2501 // This is a dynamic call to a statically known target. For example: |
| 2493 // class Foo { Function bar; } | 2502 // class Foo { Function bar; } |
| 2494 // new Foo().bar(); // dynamic call | 2503 // new Foo().bar(); // dynamic call |
| (...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2827 // Static and instance fields are handled elsewhere. | 2836 // Static and instance fields are handled elsewhere. |
| 2828 assert(node.element is TopLevelVariableElement); | 2837 assert(node.element is TopLevelVariableElement); |
| 2829 return _emitTopLevelField(node); | 2838 return _emitTopLevelField(node); |
| 2830 } | 2839 } |
| 2831 | 2840 |
| 2832 var name = | 2841 var name = |
| 2833 new JS.Identifier(node.name.name, type: emitTypeRef(node.element.type)); | 2842 new JS.Identifier(node.name.name, type: emitTypeRef(node.element.type)); |
| 2834 return new JS.VariableInitialization(name, _visitInitializer(node)); | 2843 return new JS.VariableInitialization(name, _visitInitializer(node)); |
| 2835 } | 2844 } |
| 2836 | 2845 |
| 2837 bool _isFinalJSDecl(AstNode field) => | |
| 2838 field is VariableDeclaration && | |
| 2839 field.isFinal && | |
| 2840 _isJSInvocation(field.initializer); | |
| 2841 | |
| 2842 /// Try to emit a constant static field. | 2846 /// Try to emit a constant static field. |
| 2843 /// | 2847 /// |
| 2844 /// If the field's initializer does not cause side effects, and if all of | 2848 /// If the field's initializer does not cause side effects, and if all of |
| 2845 /// dependencies are safe to refer to while we are initializing the class, | 2849 /// dependencies are safe to refer to while we are initializing the class, |
| 2846 /// then we can initialize it eagerly: | 2850 /// then we can initialize it eagerly: |
| 2847 /// | 2851 /// |
| 2848 /// // Baz must be const constructor, and the name "Baz" must be defined | 2852 /// // Baz must be const constructor, and the name "Baz" must be defined |
| 2849 /// // by this point. | 2853 /// // by this point. |
| 2850 /// Foo.bar = dart.const(new Baz(42)); | 2854 /// Foo.bar = dart.const(new Baz(42)); |
| 2851 /// | 2855 /// |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2897 jsInit = _visitInitializer(field); | 2901 jsInit = _visitInitializer(field); |
| 2898 _loader.finishTopLevel(element); | 2902 _loader.finishTopLevel(element); |
| 2899 eagerInit = _loader.isLoaded(element); | 2903 eagerInit = _loader.isLoaded(element); |
| 2900 } else { | 2904 } else { |
| 2901 // TODO(jmesserly): we're visiting the initializer here, and again | 2905 // TODO(jmesserly): we're visiting the initializer here, and again |
| 2902 // later on when we emit lazy fields. That seems busted. | 2906 // later on when we emit lazy fields. That seems busted. |
| 2903 jsInit = _visitInitializer(field); | 2907 jsInit = _visitInitializer(field); |
| 2904 eagerInit = false; | 2908 eagerInit = false; |
| 2905 } | 2909 } |
| 2906 | 2910 |
| 2907 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile | 2911 // Treat dart:runtime stuff as safe to eagerly evaluate. |
| 2908 // runtime helpers. | 2912 // TODO(jmesserly): it'd be nice to avoid this special case. |
| 2909 // TODO(jmesserly): we don't track dependencies correctly for these. | 2913 var isJSTopLevel = field.isFinal && _isDartRuntime(element.library); |
| 2910 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); | |
| 2911 if (eagerInit || isJSTopLevel) { | 2914 if (eagerInit || isJSTopLevel) { |
| 2912 // Remember that we emitted it this way, so re-export can take advantage | 2915 // Remember that we emitted it this way, so re-export can take advantage |
| 2913 // of this fact. | 2916 // of this fact. |
| 2914 _eagerTopLevelFields.add(element); | 2917 _eagerTopLevelFields.add(element); |
| 2915 | 2918 |
| 2916 return annotate( | 2919 return annotate( |
| 2917 js.statement('# = #;', [_emitTopLevelName(element), jsInit]), | 2920 js.statement('# = #;', [_emitTopLevelName(element), jsInit]), |
| 2918 field, | 2921 field, |
| 2919 element); | 2922 element); |
| 2920 } | 2923 } |
| (...skipping 672 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3593 } | 3596 } |
| 3594 } | 3597 } |
| 3595 | 3598 |
| 3596 /// Everything in Dart is an Object and supports the 4 members on Object, | 3599 /// Everything in Dart is an Object and supports the 4 members on Object, |
| 3597 /// so we have to use a runtime helper to handle values such as `null` and | 3600 /// so we have to use a runtime helper to handle values such as `null` and |
| 3598 /// native types. | 3601 /// native types. |
| 3599 /// | 3602 /// |
| 3600 /// For example `null.toString()` is legal in Dart, so we need to generate | 3603 /// For example `null.toString()` is legal in Dart, so we need to generate |
| 3601 /// that as `dart.toString(obj)`. | 3604 /// that as `dart.toString(obj)`. |
| 3602 bool _isObjectMemberCall(Expression target, String memberName) { | 3605 bool _isObjectMemberCall(Expression target, String memberName) { |
| 3603 // Is this a member on `Object`? | 3606 if (!isObjectMember(memberName)) { |
| 3604 if (!isObjectProperty(memberName)) { | |
| 3605 return false; | 3607 return false; |
| 3606 } | 3608 } |
| 3607 | 3609 |
| 3608 // Check if the target could be `null`, is dynamic, or may be an extension | 3610 // Check if the target could be `null`, is dynamic, or may be an extension |
| 3609 // native type. In all of those cases we need defensive code generation. | 3611 // native type. In all of those cases we need defensive code generation. |
| 3610 var type = getStaticType(target); | 3612 var type = getStaticType(target); |
| 3611 return isNullable(target) || | 3613 return isNullable(target) || |
| 3612 type.isDynamic || | 3614 type.isDynamic || |
| 3613 (_extensionTypes.hasNativeSubtype(type) && target is! SuperExpression); | 3615 (_extensionTypes.hasNativeSubtype(type) && target is! SuperExpression); |
| 3614 } | 3616 } |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3672 // subclasses cannot override x. | 3674 // subclasses cannot override x. |
| 3673 jsTarget = new JS.This(); | 3675 jsTarget = new JS.This(); |
| 3674 } | 3676 } |
| 3675 | 3677 |
| 3676 JS.Expression result; | 3678 JS.Expression result; |
| 3677 if (member != null && member is MethodElement && !isStatic) { | 3679 if (member != null && member is MethodElement && !isStatic) { |
| 3678 // Tear-off methods: explicitly bind it. | 3680 // Tear-off methods: explicitly bind it. |
| 3679 if (isSuper) { | 3681 if (isSuper) { |
| 3680 result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]); | 3682 result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]); |
| 3681 } else if (_isObjectMemberCall(target, memberName)) { | 3683 } else if (_isObjectMemberCall(target, memberName)) { |
| 3682 result = js.call('dart.bind(#, #, dart.#)', [jsTarget, name, name]); | 3684 result = js.call('dart.bind(#, #, dart.#)', |
| 3685 [jsTarget, _propertyName(memberName), memberName]); | |
| 3683 } else { | 3686 } else { |
| 3684 result = js.call('dart.bind(#, #)', [jsTarget, name]); | 3687 result = js.call('dart.bind(#, #)', [jsTarget, name]); |
| 3685 } | 3688 } |
| 3686 } else if (_isObjectMemberCall(target, memberName)) { | 3689 } else if (_isObjectMemberCall(target, memberName)) { |
| 3687 result = js.call('dart.#(#)', [name, jsTarget]); | 3690 result = js.call('dart.#(#)', [memberName, jsTarget]); |
| 3688 } else { | 3691 } else { |
| 3689 result = js.call('#.#', [jsTarget, name]); | 3692 result = js.call('#.#', [jsTarget, name]); |
| 3690 } | 3693 } |
| 3691 if (typeArgs == null) { | 3694 if (typeArgs == null) { |
| 3692 return result; | 3695 return result; |
| 3693 } | 3696 } |
| 3694 return js.call('dart.gbind(#, #)', [result, typeArgs]); | 3697 return js.call('dart.gbind(#, #)', [result, typeArgs]); |
| 3695 } | 3698 } |
| 3696 | 3699 |
| 3697 /// Emits a generic send, like an operator method. | 3700 /// Emits a generic send, like an operator method. |
| (...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4145 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 4148 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
| 4146 if (nodes == null || nodes.isEmpty) return null; | 4149 if (nodes == null || nodes.isEmpty) return null; |
| 4147 return new JS.Expression.binary( | 4150 return new JS.Expression.binary( |
| 4148 _visitList(nodes) as List<JS.Expression>, operator); | 4151 _visitList(nodes) as List<JS.Expression>, operator); |
| 4149 } | 4152 } |
| 4150 | 4153 |
| 4151 /// Like [_emitMemberName], but for declaration sites. | 4154 /// Like [_emitMemberName], but for declaration sites. |
| 4152 /// | 4155 /// |
| 4153 /// Unlike call sites, we always have an element available, so we can use it | 4156 /// Unlike call sites, we always have an element available, so we can use it |
| 4154 /// directly rather than computing the relevant options for [_emitMemberName]. | 4157 /// directly rather than computing the relevant options for [_emitMemberName]. |
| 4155 JS.Expression _elementMemberName(ExecutableElement e, | 4158 JS.Expression _elementMemberName(ExecutableElement e, {bool useExtension}) { |
| 4156 {bool allowExtensions: true}) { | |
| 4157 String name; | 4159 String name; |
| 4158 if (e is PropertyAccessorElement) { | 4160 if (e is PropertyAccessorElement) { |
| 4159 name = e.variable.name; | 4161 name = e.variable.name; |
| 4160 } else { | 4162 } else { |
| 4161 name = e.name; | 4163 name = e.name; |
| 4162 } | 4164 } |
| 4163 return _emitMemberName(name, | 4165 return _emitMemberName(name, |
| 4164 type: (e.enclosingElement as ClassElement).type, | 4166 type: (e.enclosingElement as ClassElement).type, |
| 4165 unary: e.parameters.isEmpty, | 4167 unary: e.parameters.isEmpty, |
| 4166 isStatic: e.isStatic, | 4168 isStatic: e.isStatic, |
| 4167 allowExtensions: allowExtensions); | 4169 useExtension: useExtension); |
| 4168 } | 4170 } |
| 4169 | 4171 |
| 4170 /// This handles member renaming for private names and operators. | 4172 /// This handles member renaming for private names and operators. |
| 4171 /// | 4173 /// |
| 4172 /// Private names are generated using ES6 symbols: | 4174 /// Private names are generated using ES6 symbols: |
| 4173 /// | 4175 /// |
| 4174 /// // At the top of the module: | 4176 /// // At the top of the module: |
| 4175 /// let _x = Symbol('_x'); | 4177 /// let _x = Symbol('_x'); |
| 4176 /// let _y = Symbol('_y'); | 4178 /// let _y = Symbol('_y'); |
| 4177 /// ... | 4179 /// ... |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 4205 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed | 4207 /// Unary minus looks like: `x['unary-']()`. Note that [unary] must be passed |
| 4206 /// for this transformation to happen, otherwise binary minus is assumed. | 4208 /// for this transformation to happen, otherwise binary minus is assumed. |
| 4207 /// | 4209 /// |
| 4208 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 4210 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
| 4209 /// helper, that checks for null. The user defined method is called '=='. | 4211 /// helper, that checks for null. The user defined method is called '=='. |
| 4210 /// | 4212 /// |
| 4211 JS.Expression _emitMemberName(String name, | 4213 JS.Expression _emitMemberName(String name, |
| 4212 {DartType type, | 4214 {DartType type, |
| 4213 bool unary: false, | 4215 bool unary: false, |
| 4214 bool isStatic: false, | 4216 bool isStatic: false, |
| 4215 bool allowExtensions: true}) { | 4217 bool useExtension}) { |
| 4216 // Static members skip the rename steps. | 4218 // Static members skip the rename steps. |
| 4217 if (isStatic) return _propertyName(name); | 4219 if (isStatic) return _propertyName(name); |
| 4218 | 4220 |
| 4219 if (name.startsWith('_')) { | 4221 if (name.startsWith('_')) { |
| 4220 return _emitPrivateNameSymbol(currentLibrary, name); | 4222 return _emitPrivateNameSymbol(currentLibrary, name); |
| 4221 } | 4223 } |
| 4222 | 4224 |
| 4223 if (name == '[]') { | 4225 if (name == '[]') { |
| 4224 name = 'get'; | 4226 name = 'get'; |
| 4225 } else if (name == '[]=') { | 4227 } else if (name == '[]=') { |
| 4226 name = 'set'; | 4228 name = 'set'; |
| 4227 } else if (name == '-' && unary) { | 4229 } else if (name == '-' && unary) { |
| 4228 name = 'unary-'; | 4230 name = 'unary-'; |
| 4229 } else if (name == 'constructor' || name == 'prototype') { | 4231 } else if (name == 'constructor' || name == 'prototype') { |
| 4230 // This uses an illegal (in Dart) character for a member, avoiding the | 4232 // This uses an illegal (in Dart) character for a member, avoiding the |
| 4231 // conflict. We could use practically any character for this. | 4233 // conflict. We could use practically any character for this. |
| 4232 name = '+$name'; | 4234 name = '+$name'; |
| 4233 } | 4235 } |
| 4234 | 4236 |
| 4235 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 4237 var result = _propertyName(name); |
| 4236 var baseType = type; | 4238 |
| 4237 while (baseType is TypeParameterType) { | 4239 if (useExtension == null) { |
| 4238 baseType = baseType.element.bound; | 4240 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
| 4239 } | 4241 var baseType = type; |
| 4240 if (allowExtensions && | 4242 while (baseType is TypeParameterType) { |
| 4241 baseType != null && | 4243 baseType = baseType.element.bound; |
| 4242 _extensionTypes.hasNativeSubtype(baseType) && | 4244 } |
| 4243 !isObjectProperty(name)) { | 4245 useExtension = baseType != null && |
| 4244 return js.call('dartx.#', _propertyName(name)); | 4246 _extensionTypes.hasNativeSubtype(baseType) && |
| 4247 !isObjectMember(name); | |
| 4245 } | 4248 } |
| 4246 | 4249 |
| 4247 return _propertyName(name); | 4250 return useExtension ? js.call('dartx.#', result) : result; |
| 4248 } | 4251 } |
| 4249 | 4252 |
| 4250 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { | 4253 JS.TemporaryId _emitPrivateNameSymbol(LibraryElement library, String name) { |
| 4251 return _privateNames | 4254 return _privateNames |
| 4252 .putIfAbsent(library, () => new HashMap()) | 4255 .putIfAbsent(library, () => new HashMap()) |
| 4253 .putIfAbsent(name, () { | 4256 .putIfAbsent(name, () { |
| 4254 var id = new JS.TemporaryId(name); | 4257 var id = new JS.TemporaryId(name); |
| 4255 _moduleItems.add( | 4258 _moduleItems.add( |
| 4256 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); | 4259 js.statement('const # = Symbol(#);', [id, js.string(id.name, "'")])); |
| 4257 return id; | 4260 return id; |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 4283 } | 4286 } |
| 4284 | 4287 |
| 4285 /// Returns true if this is any kind of object represented by `Number` in JS. | 4288 /// Returns true if this is any kind of object represented by `Number` in JS. |
| 4286 /// | 4289 /// |
| 4287 /// In practice, this is 4 types: num, int, double, and JSNumber. | 4290 /// In practice, this is 4 types: num, int, double, and JSNumber. |
| 4288 /// | 4291 /// |
| 4289 /// JSNumber is the type that actually "implements" all numbers, hence it's | 4292 /// JSNumber is the type that actually "implements" all numbers, hence it's |
| 4290 /// a subtype of int and double (and num). It's in our "dart:_interceptors". | 4293 /// a subtype of int and double (and num). It's in our "dart:_interceptors". |
| 4291 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType); | 4294 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, types.numType); |
| 4292 | 4295 |
| 4293 bool _isObjectGetter(String name) { | 4296 /// Return true if this is one of the methods/properties on all Dart Objects |
| 4294 PropertyAccessorElement element = types.objectType.element.getGetter(name); | 4297 /// (toString, hashCode, noSuchMethod, runtimeType). |
| 4295 return (element != null && !element.isStatic); | 4298 /// |
| 4296 } | 4299 /// Operator == is excluded, as it is handled as part of the equality binary |
| 4297 | 4300 /// operator. |
| 4298 bool _isObjectMethod(String name) { | 4301 bool isObjectMember(String name) { |
| 4299 MethodElement element = types.objectType.element.getMethod(name); | 4302 // We could look these up on Object, but we have hard coded runtime helpers |
| 4300 return (element != null && !element.isStatic); | 4303 // so it's not really providing any benefit. |
| 4301 } | 4304 switch (name) { |
| 4302 | 4305 case 'hashCode': |
| 4303 bool isObjectProperty(String name) { | 4306 case 'toString': |
| 4304 return _isObjectGetter(name) || _isObjectMethod(name); | 4307 case 'noSuchMethod': |
| 4308 case 'runtimeType': | |
| 4309 return true; | |
| 4310 } | |
| 4311 return false; | |
| 4305 } | 4312 } |
| 4306 | 4313 |
| 4307 // TODO(leafp): Various analyzer pieces computed similar things. | 4314 // TODO(leafp): Various analyzer pieces computed similar things. |
| 4308 // Share this logic somewhere? | 4315 // Share this logic somewhere? |
| 4309 DartType _getExpectedReturnType(ExecutableElement element) { | 4316 DartType _getExpectedReturnType(ExecutableElement element) { |
| 4310 FunctionType functionType = element.type; | 4317 FunctionType functionType = element.type; |
| 4311 if (functionType == null) { | 4318 if (functionType == null) { |
| 4312 return DynamicTypeImpl.instance; | 4319 return DynamicTypeImpl.instance; |
| 4313 } | 4320 } |
| 4314 var type = functionType.returnType; | 4321 var type = functionType.returnType; |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4394 } | 4401 } |
| 4395 | 4402 |
| 4396 bool isLibraryPrefix(Expression node) => | 4403 bool isLibraryPrefix(Expression node) => |
| 4397 node is SimpleIdentifier && node.staticElement is PrefixElement; | 4404 node is SimpleIdentifier && node.staticElement is PrefixElement; |
| 4398 | 4405 |
| 4399 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 4406 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
| 4400 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 4407 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
| 4401 | 4408 |
| 4402 bool _isDartRuntime(LibraryElement l) => | 4409 bool _isDartRuntime(LibraryElement l) => |
| 4403 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 4410 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
| OLD | NEW |