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 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
109 FunctionBody _currentFunction; | 109 FunctionBody _currentFunction; |
110 | 110 |
111 /// Helper class for emitting elements in the proper order to allow | 111 /// Helper class for emitting elements in the proper order to allow |
112 /// JS to load the module. | 112 /// JS to load the module. |
113 ElementLoader _loader; | 113 ElementLoader _loader; |
114 | 114 |
115 BuildUnit _buildUnit; | 115 BuildUnit _buildUnit; |
116 | 116 |
117 String _buildRoot; | 117 String _buildRoot; |
118 | 118 |
119 bool _superAllowed = true; | |
120 | |
121 List<JS.Method> _superHelpers = <JS.Method>[]; | |
122 | |
119 CodeGenerator(AnalysisContext c, this.options, this._extensionTypes) | 123 CodeGenerator(AnalysisContext c, this.options, this._extensionTypes) |
120 : context = c, | 124 : context = c, |
121 types = c.typeProvider, | 125 types = c.typeProvider, |
122 _asyncStreamIterator = | 126 _asyncStreamIterator = |
123 _getLibrary(c, 'dart:async').getType('StreamIterator').type, | 127 _getLibrary(c, 'dart:async').getType('StreamIterator').type, |
124 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), | 128 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), |
125 dartCoreLibrary = _getLibrary(c, 'dart:core'), | 129 dartCoreLibrary = _getLibrary(c, 'dart:core'), |
126 dartJSLibrary = _getLibrary(c, 'dart:js'); | 130 dartJSLibrary = _getLibrary(c, 'dart:js'); |
127 | 131 |
128 LibraryElement get currentLibrary => _loader.currentElement.library; | 132 LibraryElement get currentLibrary => _loader.currentElement.library; |
(...skipping 696 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
825 } | 829 } |
826 | 830 |
827 bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null; | 831 bool hasJsPeer = findAnnotation(element, isJsPeerInterface) != null; |
828 | 832 |
829 bool hasIterator = false; | 833 bool hasIterator = false; |
830 for (var m in node.members) { | 834 for (var m in node.members) { |
831 if (m is ConstructorDeclaration) { | 835 if (m is ConstructorDeclaration) { |
832 jsMethods | 836 jsMethods |
833 .add(_emitConstructor(m, type, fields, virtualFields, isObject)); | 837 .add(_emitConstructor(m, type, fields, virtualFields, isObject)); |
834 } else if (m is MethodDeclaration) { | 838 } else if (m is MethodDeclaration) { |
835 jsMethods.add(_emitMethodDeclaration(type, m)); | 839 jsMethods.add(_emitMethodDeclaration(m)); |
836 | 840 |
837 if (m.element is PropertyAccessorElement) { | 841 if (m.element is PropertyAccessorElement) { |
838 jsMethods.add(_emitSuperAccessorWrapper(m, type, superclasses)); | 842 jsMethods.add(_emitSuperAccessorWrapper(m, type, superclasses)); |
839 } | 843 } |
840 | 844 |
841 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { | 845 if (!hasJsPeer && m.isGetter && m.name.name == 'iterator') { |
842 hasIterator = true; | 846 hasIterator = true; |
843 jsMethods.add(_emitIterable(type)); | 847 jsMethods.add(_emitIterable(type)); |
844 } | 848 } |
845 } else if (m is FieldDeclaration) { | 849 } else if (m is FieldDeclaration) { |
(...skipping 15 matching lines...) Expand all Loading... | |
861 // helper on a parent class. This pattern is common in the core libraries | 865 // helper on a parent class. This pattern is common in the core libraries |
862 // (e.g. IterableMixin<E> and IterableBase<E>). | 866 // (e.g. IterableMixin<E> and IterableBase<E>). |
863 // | 867 // |
864 // (We could do this same optimization for any interface with an `iterator` | 868 // (We could do this same optimization for any interface with an `iterator` |
865 // method, but that's more expensive to check for, so it doesn't seem worth | 869 // method, but that's more expensive to check for, so it doesn't seem worth |
866 // it. The above case for an explicit `iterator` method will catch those.) | 870 // it. The above case for an explicit `iterator` method will catch those.) |
867 if (!hasJsPeer && !hasIterator && _implementsIterable(type)) { | 871 if (!hasJsPeer && !hasIterator && _implementsIterable(type)) { |
868 jsMethods.add(_emitIterable(type)); | 872 jsMethods.add(_emitIterable(type)); |
869 } | 873 } |
870 | 874 |
875 // Add all of the super helper methods | |
876 jsMethods.addAll(_superHelpers); | |
877 _superHelpers.clear(); | |
878 | |
871 return jsMethods.where((m) => m != null).toList(growable: false); | 879 return jsMethods.where((m) => m != null).toList(growable: false); |
872 } | 880 } |
873 | 881 |
874 /// This is called whenever a derived class needs to introduce a new field, | 882 /// This is called whenever a derived class needs to introduce a new field, |
875 /// shadowing a field or getter/setter pair on its parent. | 883 /// shadowing a field or getter/setter pair on its parent. |
876 /// | 884 /// |
877 /// This is important because otherwise, trying to read or write the field | 885 /// This is important because otherwise, trying to read or write the field |
878 /// would end up calling the getter or setter, and one of those might not even | 886 /// would end up calling the getter or setter, and one of those might not even |
879 /// exist, resulting in a runtime error. Even if they did exist, that's the | 887 /// exist, resulting in a runtime error. Even if they did exist, that's the |
880 /// wrong behavior if a new field was declared. | 888 /// wrong behavior if a new field was declared. |
(...skipping 728 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1609 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); | 1617 return new JS.Fun(params, js.statement('{ return this.#; }', [name])); |
1610 } else if (node.isSetter) { | 1618 } else if (node.isSetter) { |
1611 return new JS.Fun( | 1619 return new JS.Fun( |
1612 params, js.statement('{ this.# = #; }', [name, params.last])); | 1620 params, js.statement('{ this.# = #; }', [name, params.last])); |
1613 } else { | 1621 } else { |
1614 return new JS.Fun( | 1622 return new JS.Fun( |
1615 params, js.statement('{ return this.#(#); }', [name, params])); | 1623 params, js.statement('{ return this.#(#); }', [name, params])); |
1616 } | 1624 } |
1617 } | 1625 } |
1618 | 1626 |
1619 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1627 JS.Method _emitMethodDeclaration(MethodDeclaration node) { |
1620 if (node.isAbstract) { | 1628 if (node.isAbstract) { |
1621 return null; | 1629 return null; |
1622 } | 1630 } |
1623 | 1631 |
1624 JS.Fun fn; | 1632 JS.Fun fn; |
1625 if (_externalOrNative(node)) { | 1633 if (_externalOrNative(node)) { |
1626 fn = _emitNativeFunctionBody(node); | 1634 fn = _emitNativeFunctionBody(node); |
1627 // TODO(vsm): Remove if / when we handle the static case above. | 1635 // TODO(vsm): Remove if / when we handle the static case above. |
1628 if (fn == null) return null; | 1636 if (fn == null) return null; |
1629 } else { | 1637 } else { |
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1932 // `await` is generated as `yield`. | 1940 // `await` is generated as `yield`. |
1933 // runtime/_generators.js has an example of what the code is generated as. | 1941 // runtime/_generators.js has an example of what the code is generated as. |
1934 var savedController = _asyncStarController; | 1942 var savedController = _asyncStarController; |
1935 List jsParams = visitFormalParameterList(parameters); | 1943 List jsParams = visitFormalParameterList(parameters); |
1936 if (kind == 'asyncStar') { | 1944 if (kind == 'asyncStar') { |
1937 _asyncStarController = new JS.TemporaryId('stream'); | 1945 _asyncStarController = new JS.TemporaryId('stream'); |
1938 jsParams.insert(0, _asyncStarController); | 1946 jsParams.insert(0, _asyncStarController); |
1939 } else { | 1947 } else { |
1940 _asyncStarController = null; | 1948 _asyncStarController = null; |
1941 } | 1949 } |
1950 var savedSuperAllowed = _superAllowed; | |
1951 _superAllowed = false; | |
1942 // Visit the body with our async* controller set. | 1952 // Visit the body with our async* controller set. |
1943 var jsBody = _visit(body); | 1953 var jsBody = _visit(body); |
1954 _superAllowed = savedSuperAllowed; | |
1944 _asyncStarController = savedController; | 1955 _asyncStarController = savedController; |
1945 | 1956 |
1946 DartType returnType = _getExpectedReturnType(element); | 1957 DartType returnType = _getExpectedReturnType(element); |
1947 JS.Expression gen = new JS.Fun(jsParams, jsBody, | 1958 JS.Expression gen = new JS.Fun(jsParams, jsBody, |
1948 isGenerator: true, returnType: emitTypeRef(returnType)); | 1959 isGenerator: true, returnType: emitTypeRef(returnType)); |
1949 if (JS.This.foundIn(gen)) { | 1960 if (JS.This.foundIn(gen)) { |
1950 gen = js.call('#.bind(this)', gen); | 1961 gen = js.call('#.bind(this)', gen); |
1951 } | 1962 } |
1952 | 1963 |
1953 var T = _emitTypeName(returnType); | 1964 var T = _emitTypeName(returnType); |
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2327 | 2338 |
2328 var target = _getTarget(node); | 2339 var target = _getTarget(node); |
2329 if (target == null || isLibraryPrefix(target)) { | 2340 if (target == null || isLibraryPrefix(target)) { |
2330 return _emitFunctionCall(node); | 2341 return _emitFunctionCall(node); |
2331 } | 2342 } |
2332 | 2343 |
2333 return _emitMethodCall(target, node); | 2344 return _emitMethodCall(target, node); |
2334 } | 2345 } |
2335 | 2346 |
2336 /// Emits a (possibly generic) instance method call. | 2347 /// Emits a (possibly generic) instance method call. |
2337 JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { | 2348 JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { |
Jennifer Messerly
2016/05/03 18:56:18
do we need the same fix in _emitAccess?
Harry Terkelsen
2016/05/05 23:37:34
Done.
| |
2338 var type = getStaticType(target); | 2349 var type = getStaticType(target); |
2339 var name = node.methodName.name; | 2350 var name = node.methodName.name; |
2340 var element = node.methodName.staticElement; | 2351 var element = node.methodName.staticElement; |
2341 bool isStatic = element is ExecutableElement && element.isStatic; | 2352 bool isStatic = element is ExecutableElement && element.isStatic; |
2342 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); | 2353 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); |
2343 | 2354 |
2344 JS.Expression jsTarget = _visit(target); | 2355 JS.Expression jsTarget = _visit(target); |
2345 var typeArgs = _emitInvokeTypeArguments(node); | 2356 var typeArgs = _emitInvokeTypeArguments(node); |
2346 List<JS.Expression> args = _visit(node.argumentList); | 2357 List<JS.Expression> args = _visit(node.argumentList); |
2347 if (DynamicInvoke.get(target)) { | 2358 if (DynamicInvoke.get(target)) { |
2348 if (typeArgs != null) { | 2359 if (typeArgs != null) { |
2349 return js.call('dart.dgsend(#, #, #, #)', | 2360 return js.call('dart.dgsend(#, #, #, #)', |
2350 [jsTarget, new JS.ArrayInitializer(typeArgs), memberName, args]); | 2361 [jsTarget, new JS.ArrayInitializer(typeArgs), memberName, args]); |
2351 } else { | 2362 } else { |
2352 return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); | 2363 return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); |
2353 } | 2364 } |
2354 } | 2365 } |
2355 if (_isObjectMemberCall(target, name)) { | 2366 if (_isObjectMemberCall(target, name)) { |
2356 // Object methods require a helper for null checks & native types. | 2367 // Object methods require a helper for null checks & native types. |
2357 assert(typeArgs == null); // Object methods don't take type args. | 2368 assert(typeArgs == null); // Object methods don't take type args. |
2358 return js.call('dart.#(#, #)', [memberName, jsTarget, args]); | 2369 return js.call('dart.#(#, #)', [memberName, jsTarget, args]); |
2359 } | 2370 } |
2360 | 2371 |
2361 jsTarget = new JS.PropertyAccess(jsTarget, memberName); | 2372 jsTarget = new JS.PropertyAccess(jsTarget, memberName); |
2373 | |
2374 if (target is SuperExpression && !_superAllowed) { | |
2375 return _emitSuperForwarder(node, jsTarget, typeArgs, args); | |
2376 } | |
2377 | |
2362 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); | 2378 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
2363 | 2379 |
2364 if (DynamicInvoke.get(node.methodName)) { | 2380 if (DynamicInvoke.get(node.methodName)) { |
Jennifer Messerly
2016/05/03 18:56:18
Is there a way to restructure this code so it's re
Harry Terkelsen
2016/05/05 23:37:35
There is a way ;)
| |
2365 // This is a dynamic call to a statically known target. For example: | 2381 // This is a dynamic call to a statically known target. For example: |
2366 // class Foo { Function bar; } | 2382 // class Foo { Function bar; } |
2367 // new Foo().bar(); // dynamic call | 2383 // new Foo().bar(); // dynamic call |
2368 return js.call('dart.dcall(#, #)', [jsTarget, args]); | 2384 return js.call('dart.dcall(#, #)', [jsTarget, args]); |
2369 } | 2385 } |
2370 | 2386 |
2371 return new JS.Call(jsTarget, args); | 2387 return new JS.Call(jsTarget, args); |
2372 } | 2388 } |
2373 | 2389 |
2390 JS.Expression _emitSuperForwarder( | |
2391 MethodInvocation node, | |
2392 JS.PropertyAccess jsTarget, | |
2393 List<JS.Expression> typeArgs, | |
2394 List<JS.Expression> args) { | |
2395 var helperMethod = _getSuperHelperFor(node, jsTarget, typeArgs, args); | |
2396 var newTarget = new JS.PropertyAccess(new JS.This(), helperMethod); | |
2397 var helperArgs = <JS.Expression>[]; | |
2398 if (typeArgs != null) { | |
2399 helperArgs.addAll(typeArgs); | |
2400 } | |
2401 helperArgs.addAll(args); | |
2402 return new JS.Call(newTarget, helperArgs); | |
Jennifer Messerly
2016/05/03 18:56:18
FYI: you could build these like:
return js.call('
Harry Terkelsen
2016/05/05 23:37:34
Done.
| |
2403 } | |
2404 | |
2405 JS.Expression _getSuperHelperFor( | |
Jennifer Messerly
2016/05/03 18:56:18
Probably worth a doc comment about what this metho
| |
2406 MethodInvocation node, | |
2407 JS.PropertyAccess jsTarget, | |
2408 List<JS.Expression> typeArgs, | |
Jennifer Messerly
2016/05/03 18:56:18
Hmmm, this is an interesting approach. It's one su
| |
2409 List<JS.Expression> args) { | |
2410 var counter = 0; | |
2411 var helperArgs = | |
2412 new List.generate(typeArgs?.length ?? 0 + args.length, (index) { | |
2413 new JS.Identifier('a${counter++}'); | |
Jennifer Messerly
2016/05/03 18:56:18
this should be symbolized so it doesn't collide wi
Harry Terkelsen
2016/05/05 23:37:34
Done.
| |
2414 }, growable: false); | |
2415 | |
2416 var newTarget; | |
2417 if (typeArgs != null) { | |
2418 newTarget = new JS.Call(jsTarget, helperArgs.sublist(0, typeArgs.length)); | |
2419 } else { | |
2420 newTarget = jsTarget; | |
2421 } | |
2422 | |
2423 var forwardedCall; | |
2424 if (DynamicInvoke.get(node.methodName)) { | |
2425 forwardedCall = js.call('dart.dcall(#, #)', | |
2426 [newTarget, helperArgs.sublist(typeArgs?.length ?? 0)]); | |
2427 } else { | |
2428 forwardedCall = | |
2429 new JS.Call(newTarget, helperArgs.sublist(typeArgs?.length ?? 0)); | |
2430 } | |
2431 | |
2432 var helperMethod = | |
2433 new JS.Fun(helperArgs, new JS.Block([new JS.Return(forwardedCall)])); | |
2434 var helperMethodName = | |
2435 js.string('super\$${node.methodName.name}${_superHelpers.length}', "'"); | |
Jennifer Messerly
2016/05/03 18:56:18
based on comment above, "helperMethodName" will ju
Harry Terkelsen
2016/05/05 23:37:34
Done.
| |
2436 _superHelpers.add(new JS.Method(helperMethodName, helperMethod)); | |
2437 return helperMethodName; | |
2438 } | |
2439 | |
2374 /// Emits a function call, to a top-level function, local function, or | 2440 /// Emits a function call, to a top-level function, local function, or |
2375 /// an expression. | 2441 /// an expression. |
2376 JS.Expression _emitFunctionCall(InvocationExpression node) { | 2442 JS.Expression _emitFunctionCall(InvocationExpression node) { |
2377 var fn = _visit(node.function); | 2443 var fn = _visit(node.function); |
2378 var args = _visit(node.argumentList); | 2444 var args = _visit(node.argumentList); |
2379 if (DynamicInvoke.get(node.function)) { | 2445 if (DynamicInvoke.get(node.function)) { |
2380 var typeArgs = _emitInvokeTypeArguments(node); | 2446 var typeArgs = _emitInvokeTypeArguments(node); |
2381 if (typeArgs != null) { | 2447 if (typeArgs != null) { |
2382 return js.call('dart.dgcall(#, #, #)', | 2448 return js.call('dart.dgcall(#, #, #)', |
2383 [fn, new JS.ArrayInitializer(typeArgs), args]); | 2449 [fn, new JS.ArrayInitializer(typeArgs), args]); |
(...skipping 1332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3716 } | 3782 } |
3717 | 3783 |
3718 @override | 3784 @override |
3719 visitContinueStatement(ContinueStatement node) { | 3785 visitContinueStatement(ContinueStatement node) { |
3720 var label = node.label; | 3786 var label = node.label; |
3721 return new JS.Continue(label?.name); | 3787 return new JS.Continue(label?.name); |
3722 } | 3788 } |
3723 | 3789 |
3724 @override | 3790 @override |
3725 visitTryStatement(TryStatement node) { | 3791 visitTryStatement(TryStatement node) { |
3726 return new JS.Try(_visit(node.body), _visitCatch(node.catchClauses), | 3792 var savedSuperAllowed = _superAllowed; |
3727 _visit(node.finallyBlock)); | 3793 _superAllowed = false; |
3794 var finallyBlock = _visit(node.finallyBlock); | |
3795 _superAllowed = savedSuperAllowed; | |
3796 return new JS.Try( | |
3797 _visit(node.body), _visitCatch(node.catchClauses), finallyBlock); | |
3728 } | 3798 } |
3729 | 3799 |
3730 _visitCatch(NodeList<CatchClause> clauses) { | 3800 _visitCatch(NodeList<CatchClause> clauses) { |
3731 if (clauses == null || clauses.isEmpty) return null; | 3801 if (clauses == null || clauses.isEmpty) return null; |
3732 | 3802 |
3733 // TODO(jmesserly): need a better way to get a temporary variable. | 3803 // TODO(jmesserly): need a better way to get a temporary variable. |
3734 // This could incorrectly shadow a user's name. | 3804 // This could incorrectly shadow a user's name. |
3735 var savedCatch = _catchParameter; | 3805 var savedCatch = _catchParameter; |
3736 | 3806 |
3737 if (clauses.length == 1 && clauses.single.exceptionParameter != null) { | 3807 if (clauses.length == 1 && clauses.single.exceptionParameter != null) { |
(...skipping 487 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4225 } | 4295 } |
4226 | 4296 |
4227 bool isLibraryPrefix(Expression node) => | 4297 bool isLibraryPrefix(Expression node) => |
4228 node is SimpleIdentifier && node.staticElement is PrefixElement; | 4298 node is SimpleIdentifier && node.staticElement is PrefixElement; |
4229 | 4299 |
4230 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 4300 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
4231 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 4301 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
4232 | 4302 |
4233 bool _isDartRuntime(LibraryElement l) => | 4303 bool _isDartRuntime(LibraryElement l) => |
4234 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 4304 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
OLD | NEW |