Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(232)

Side by Side Diff: lib/src/compiler/code_generator.dart

Issue 1948563002: allow 'super' in async and finally blocks (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | lib/src/compiler/source_map_printer.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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';
OLDNEW
« no previous file with comments | « no previous file | lib/src/compiler/source_map_printer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698