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 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
6 | 6 |
7 import 'dart:collection' show HashSet, HashMap; | 7 import 'dart:collection' show HashSet, HashMap; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
(...skipping 1011 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1022 (element.library != libraryInfo.library || | 1022 (element.library != libraryInfo.library || |
1023 element is TopLevelVariableElement && !element.isConst)) { | 1023 element is TopLevelVariableElement && !element.isConst)) { |
1024 return js.call('#.#', [_libraryName(element.library), name]); | 1024 return js.call('#.#', [_libraryName(element.library), name]); |
1025 } | 1025 } |
1026 | 1026 |
1027 // Unqualified class member. This could mean implicit-this, or implicit | 1027 // Unqualified class member. This could mean implicit-this, or implicit |
1028 // call to a static from the same class. | 1028 // call to a static from the same class. |
1029 if (element is ClassMemberElement && element is! ConstructorElement) { | 1029 if (element is ClassMemberElement && element is! ConstructorElement) { |
1030 bool isStatic = element.isStatic; | 1030 bool isStatic = element.isStatic; |
1031 var type = element.enclosingElement.type; | 1031 var type = element.enclosingElement.type; |
| 1032 var member = _emitMemberName(name, isStatic: isStatic, type: type); |
1032 | 1033 |
1033 // For instance methods, we add implicit-this. | |
1034 // For static methods, we add the raw type name, without generics or | 1034 // For static methods, we add the raw type name, without generics or |
1035 // library prefix. We don't need those because static calls can't use | 1035 // library prefix. We don't need those because static calls can't use |
1036 // the generic type. | 1036 // the generic type. |
1037 var target = isStatic ? new JS.Identifier(type.name) : new JS.This(); | 1037 if (isStatic) { |
1038 var member = _emitMemberName(name, isStatic: isStatic, type: type); | 1038 return js.call('#.#', [type.name, member]); |
1039 return new JS.PropertyAccess(target, member); | 1039 } |
| 1040 |
| 1041 // For instance members, we add implicit-this. |
| 1042 // For method tear-offs, we ensure it's a bound method. |
| 1043 var code = 'this.#'; |
| 1044 if (element is MethodElement && !inInvocationContext(node)) { |
| 1045 code += '.bind(this)'; |
| 1046 } |
| 1047 return js.call(code, member); |
1040 } | 1048 } |
1041 | 1049 |
1042 // initializing formal parameter, e.g. `Point(this.x)` | 1050 // initializing formal parameter, e.g. `Point(this.x)` |
1043 if (element is ParameterElement && | 1051 if (element is ParameterElement && |
1044 element.isInitializingFormal && | 1052 element.isInitializingFormal && |
1045 element.isPrivate) { | 1053 element.isPrivate) { |
1046 /// Rename private names so they don't shadow the private field symbol. | 1054 /// Rename private names so they don't shadow the private field symbol. |
1047 /// The renamer would handle this, but it would prefer to rename the | 1055 /// The renamer would handle this, but it would prefer to rename the |
1048 /// temporary used for the private symbol. Instead rename the parameter. | 1056 /// temporary used for the private symbol. Instead rename the parameter. |
1049 return _getTemp(element, '${name.substring(1)}'); | 1057 return _getTemp(element, '${name.substring(1)}'); |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1221 @override | 1229 @override |
1222 JS.Block visitBlock(Block node) => new JS.Block(_visitList(node.statements)); | 1230 JS.Block visitBlock(Block node) => new JS.Block(_visitList(node.statements)); |
1223 | 1231 |
1224 @override | 1232 @override |
1225 visitMethodInvocation(MethodInvocation node) { | 1233 visitMethodInvocation(MethodInvocation node) { |
1226 var target = node.isCascaded ? _cascadeTarget : node.target; | 1234 var target = node.isCascaded ? _cascadeTarget : node.target; |
1227 | 1235 |
1228 var result = _emitForeignJS(node); | 1236 var result = _emitForeignJS(node); |
1229 if (result != null) return result; | 1237 if (result != null) return result; |
1230 | 1238 |
1231 // TODO(jmesserly): if we try to call a getter returning a function with | |
1232 // a call method, we don't generate the `.call` correctly. | |
1233 String code; | 1239 String code; |
1234 if (target == null || isLibraryPrefix(target)) { | 1240 if (target == null || isLibraryPrefix(target)) { |
1235 if (rules.isDynamicCall(node.methodName)) { | 1241 if (rules.isDynamicCall(node.methodName)) { |
1236 code = 'dart.$DCALL(#, #)'; | 1242 code = 'dart.$DCALL(#, #)'; |
1237 } else { | 1243 } else { |
1238 code = '#(#)'; | 1244 code = '#(#)'; |
1239 } | 1245 } |
1240 return js.call( | 1246 return js.call( |
1241 code, [_visit(node.methodName), _visit(node.argumentList)]); | 1247 code, [_visit(node.methodName), _visit(node.argumentList)]); |
1242 } | 1248 } |
1243 | 1249 |
1244 // TODO(jmesserly): if the methodName resolves statically but the call is | 1250 if (rules.isDynamicTarget(target)) { |
1245 // dynamic (e.g. `obj.method` is resolved to a field of type `Function`), we | |
1246 // could generate call(#.#, #). Not sure if that's worth it. | |
1247 if (rules.isDynamicCall(node.methodName)) { | |
1248 code = 'dart.$DSEND(#, #, #)'; | 1251 code = 'dart.$DSEND(#, #, #)'; |
| 1252 } else if (rules.isDynamicCall(node.methodName)) { |
| 1253 // This is a dynamic call to a statically know target. For example: |
| 1254 // class Foo { Function bar; } |
| 1255 // new Foo().bar(); // dynamic call |
| 1256 code = 'dart.$DCALL(#.#, #)'; |
1249 } else { | 1257 } else { |
1250 code = '#.#(#)'; | 1258 code = '#.#(#)'; |
1251 } | 1259 } |
1252 return js.call(code, [ | 1260 return js.call(code, [ |
1253 _visit(target), | 1261 _visit(target), |
1254 _emitMemberName(node.methodName.name, type: getStaticType(target)), | 1262 _emitMemberName(node.methodName.name, type: getStaticType(target)), |
1255 _visit(node.argumentList) | 1263 _visit(node.argumentList) |
1256 ]); | 1264 ]); |
1257 } | 1265 } |
1258 | 1266 |
(...skipping 530 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1789 JS.This visitThisExpression(ThisExpression node) => new JS.This(); | 1797 JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
1790 | 1798 |
1791 @override | 1799 @override |
1792 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); | 1800 JS.Super visitSuperExpression(SuperExpression node) => new JS.Super(); |
1793 | 1801 |
1794 @override | 1802 @override |
1795 visitPrefixedIdentifier(PrefixedIdentifier node) { | 1803 visitPrefixedIdentifier(PrefixedIdentifier node) { |
1796 if (isLibraryPrefix(node.prefix)) { | 1804 if (isLibraryPrefix(node.prefix)) { |
1797 return _visit(node.identifier); | 1805 return _visit(node.identifier); |
1798 } else { | 1806 } else { |
1799 return _emitGet(node.prefix, node.identifier.name); | 1807 return _emitGet(node.prefix, node.identifier); |
1800 } | 1808 } |
1801 } | 1809 } |
1802 | 1810 |
1803 @override | 1811 @override |
1804 visitPropertyAccess(PropertyAccess node) => | 1812 visitPropertyAccess(PropertyAccess node) => |
1805 _emitGet(_getTarget(node), node.propertyName.name); | 1813 _emitGet(_getTarget(node), node.propertyName); |
1806 | 1814 |
1807 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 1815 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
1808 JS.Expression _emitGet(Expression target, String memberName) { | 1816 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { |
1809 var name = _emitMemberName(memberName, type: getStaticType(target)); | 1817 var name = _emitMemberName(memberId.name, type: getStaticType(target)); |
1810 if (rules.isDynamicTarget(target)) { | 1818 if (rules.isDynamicTarget(target)) { |
1811 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); | 1819 return js.call('dart.$DLOAD(#, #)', [_visit(target), name]); |
| 1820 } |
| 1821 |
| 1822 String code; |
| 1823 var member = memberId.staticElement; |
| 1824 if (member != null && member is MethodElement) { |
| 1825 // Tear-off methods: explicitly bind it. |
| 1826 if (isStateless(target, target)) { |
| 1827 return js.call('#.#.bind(#)', [_visit(target), name, _visit(target)]); |
| 1828 } |
| 1829 code = 'dart.bind(#, #)'; |
1812 } else { | 1830 } else { |
1813 return js.call('#.#', [_visit(target), name]); | 1831 code = '#.#'; |
1814 } | 1832 } |
| 1833 |
| 1834 return js.call(code, [_visit(target), name]); |
1815 } | 1835 } |
1816 | 1836 |
| 1837 /// Emits a generic send, like an operator method. |
| 1838 /// |
| 1839 /// **Please note** this function does not support method invocation syntax |
| 1840 /// `obj.name(args)` because that could be a getter followed by a call. |
| 1841 /// See [visitMethodInvocation]. |
1817 JS.Expression _emitSend( | 1842 JS.Expression _emitSend( |
1818 Expression target, String name, List<Expression> args) { | 1843 Expression target, String name, List<Expression> args) { |
1819 var type = getStaticType(target); | 1844 var type = getStaticType(target); |
1820 var memberName = _emitMemberName(name, unary: args.isEmpty, type: type); | 1845 var memberName = _emitMemberName(name, unary: args.isEmpty, type: type); |
1821 if (rules.isDynamicTarget(target)) { | 1846 if (rules.isDynamicTarget(target)) { |
1822 // dynamic dispatch | 1847 // dynamic dispatch |
1823 var dynamicHelper = const {'[]': DINDEX, '[]=': DSETINDEX}[name]; | 1848 var dynamicHelper = const {'[]': DINDEX, '[]=': DSETINDEX}[name]; |
1824 if (dynamicHelper != null) { | 1849 if (dynamicHelper != null) { |
1825 return js.call( | 1850 return js.call( |
1826 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); | 1851 'dart.$dynamicHelper(#, #)', [_visit(target), _visitList(args)]); |
1827 } | 1852 } |
1828 return js.call('dart.$DSEND(#, #, #)', [ | 1853 return js.call('dart.$DSEND(#, #, #)', [ |
1829 _visit(target), | 1854 _visit(target), |
1830 memberName, | 1855 memberName, |
1831 _visitList(args) | 1856 _visitList(args) |
1832 ]); | 1857 ]); |
1833 } | 1858 } |
| 1859 |
1834 if (_isJSBuiltinType(type)) { | 1860 if (_isJSBuiltinType(type)) { |
1835 // static call pattern for bultins. | 1861 // static call pattern for builtins. |
1836 return js.call('#.#(#, #)', [ | 1862 return js.call('#.#(#, #)', [ |
1837 _emitTypeName(type), | 1863 _emitTypeName(type), |
1838 memberName, | 1864 memberName, |
1839 _visit(target), | 1865 _visit(target), |
1840 _visitList(args) | 1866 _visitList(args) |
1841 ]); | 1867 ]); |
1842 } | 1868 } |
1843 // Generic dispatch to a statically known method. | 1869 // Generic dispatch to a statically known method. |
1844 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); | 1870 return js.call('#.#(#)', [_visit(target), memberName, _visitList(args)]); |
1845 } | 1871 } |
(...skipping 538 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2384 return filepath; | 2410 return filepath; |
2385 } | 2411 } |
2386 | 2412 |
2387 // TODO(jmesserly): validate the library. See issue #135. | 2413 // TODO(jmesserly): validate the library. See issue #135. |
2388 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; | 2414 bool _isJsNameAnnotation(DartObjectImpl value) => value.type.name == 'JsName'; |
2389 | 2415 |
2390 // TODO(jacobr): we would like to do something like the following | 2416 // TODO(jacobr): we would like to do something like the following |
2391 // but we don't have summary support yet. | 2417 // but we don't have summary support yet. |
2392 // bool _supportJsExtensionMethod(AnnotatedNode node) => | 2418 // bool _supportJsExtensionMethod(AnnotatedNode node) => |
2393 // _getAnnotation(node, "SupportJsExtensionMethod") != null; | 2419 // _getAnnotation(node, "SupportJsExtensionMethod") != null; |
OLD | NEW |