Skip to content

Commit d9e0533

Browse files
allow 'super' in async and finally blocks
Fixes #499 Fixes #528 [email protected] Review URL: https://codereview.chromium.org/1948563002 .
1 parent 7d32105 commit d9e0533

10 files changed

+288
-30
lines changed

pkg/dev_compiler/lib/runtime/dart_sdk.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25019,8 +25019,8 @@ dart_library.library('dart_sdk', null, /* Imports */[
2501925019
return _ConverterStreamEventSink;
2502025020
});
2502125021
convert._ConverterStreamEventSink = convert._ConverterStreamEventSink$();
25022-
const _first$ = Symbol('_first');
2502325022
const _second = Symbol('_second');
25023+
const _first$ = Symbol('_first');
2502425024
convert._FusedCodec$ = dart.generic((S, M, T) => {
2502525025
class _FusedCodec extends convert.Codec$(S, T) {
2502625026
get encoder() {

pkg/dev_compiler/lib/src/compiler/code_generator.dart

Lines changed: 129 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ class CodeGenerator extends GeneralizingAstVisitor
118118

119119
String _buildRoot;
120120

121+
bool _superAllowed = true;
122+
123+
List<JS.TemporaryId> _superHelperSymbols = <JS.TemporaryId>[];
124+
List<JS.Method> _superHelpers = <JS.Method>[];
125+
121126
/// Whether we are currently generating code for the body of a `JS()` call.
122127
bool _isInForeignJS = false;
123128

@@ -595,6 +600,7 @@ class CodeGenerator extends GeneralizingAstVisitor
595600
var body = <JS.Statement>[];
596601
var extensions = _extensionsToImplement(classElem);
597602
_initExtensionSymbols(classElem, methods, fields, body);
603+
_emitSuperHelperSymbols(_superHelperSymbols, body);
598604

599605
// Emit the class, e.g. `core.Object = class Object { ... }`
600606
_defineClass(classElem, className, classExpr, body);
@@ -619,6 +625,14 @@ class CodeGenerator extends GeneralizingAstVisitor
619625
return _statement(body);
620626
}
621627

628+
void _emitSuperHelperSymbols(
629+
List<JS.TemporaryId> superHelperSymbols, List<JS.Statement> body) {
630+
for (var id in superHelperSymbols) {
631+
body.add(js.statement('const # = Symbol(#)', [id, js.string(id.name)]));
632+
}
633+
superHelperSymbols.clear();
634+
}
635+
622636
void _registerPropertyOverrides(
623637
ClassElement classElem,
624638
JS.Expression className,
@@ -897,6 +911,10 @@ class CodeGenerator extends GeneralizingAstVisitor
897911
jsMethods.add(_emitIterable(type));
898912
}
899913

914+
// Add all of the super helper methods
915+
jsMethods.addAll(_superHelpers);
916+
_superHelpers.clear();
917+
900918
return jsMethods.where((m) => m != null).toList(growable: false);
901919
}
902920

@@ -1630,7 +1648,7 @@ class CodeGenerator extends GeneralizingAstVisitor
16301648
}
16311649
}
16321650

1633-
JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) {
1651+
JS.Method _emitMethodDeclaration(InterfaceType type, MethodDeclaration node) {
16341652
if (node.isAbstract) {
16351653
return null;
16361654
}
@@ -1956,8 +1974,11 @@ class CodeGenerator extends GeneralizingAstVisitor
19561974
} else {
19571975
_asyncStarController = null;
19581976
}
1977+
var savedSuperAllowed = _superAllowed;
1978+
_superAllowed = false;
19591979
// Visit the body with our async* controller set.
19601980
var jsBody = _visit(body);
1981+
_superAllowed = savedSuperAllowed;
19611982
_asyncStarController = savedController;
19621983

19631984
DartType returnType = _getExpectedReturnType(element);
@@ -2003,8 +2024,12 @@ class CodeGenerator extends GeneralizingAstVisitor
20032024
/// function instantiation.
20042025
@override
20052026
JS.Expression visitSimpleIdentifier(SimpleIdentifier node) {
2006-
return _applyFunctionTypeArguments(
2007-
_emitSimpleIdentifier(node), node.staticElement, node.staticType);
2027+
var typeArgs = _getTypeArgs(node.staticElement, node.staticType);
2028+
var simpleId = _emitSimpleIdentifier(node);
2029+
if (typeArgs == null) {
2030+
return simpleId;
2031+
}
2032+
return js.call('dart.gbind(#, #)', [simpleId, typeArgs]);
20082033
}
20092034

20102035
/// Emits a simple identifier, handling implicit `this` as well as
@@ -2384,17 +2409,66 @@ class CodeGenerator extends GeneralizingAstVisitor
23842409
return _emitMethodCall(target, node);
23852410
}
23862411

2387-
/// Emits a (possibly generic) instance method call.
23882412
JS.Expression _emitMethodCall(Expression target, MethodInvocation node) {
2413+
List<JS.Expression> args = _visit(node.argumentList);
2414+
var typeArgs = _emitInvokeTypeArguments(node);
2415+
2416+
if (target is SuperExpression && !_superAllowed) {
2417+
return _emitSuperHelperCall(typeArgs, args, target, node);
2418+
}
2419+
2420+
return _emitMethodCallInternal(target, node, args, typeArgs);
2421+
}
2422+
2423+
JS.Expression _emitSuperHelperCall(List<JS.Expression> typeArgs,
2424+
List<JS.Expression> args, SuperExpression target, MethodInvocation node) {
2425+
var fakeTypeArgs =
2426+
typeArgs?.map((_) => new JS.TemporaryId('a'))?.toList(growable: false);
2427+
var fakeArgs =
2428+
args.map((_) => new JS.TemporaryId('a')).toList(growable: false);
2429+
var combinedFakeArgs = <JS.TemporaryId>[];
2430+
if (fakeTypeArgs != null) {
2431+
combinedFakeArgs.addAll(fakeTypeArgs);
2432+
}
2433+
combinedFakeArgs.addAll(fakeArgs);
2434+
2435+
var forwardedCall =
2436+
_emitMethodCallInternal(target, node, fakeArgs, fakeTypeArgs);
2437+
var superForwarder = _getSuperHelperFor(
2438+
node.methodName.name, forwardedCall, combinedFakeArgs);
2439+
2440+
var combinedRealArgs = <JS.Expression>[];
2441+
if (typeArgs != null) {
2442+
combinedRealArgs.addAll(typeArgs);
2443+
}
2444+
combinedRealArgs.addAll(args);
2445+
2446+
return js.call('this.#(#)', [superForwarder, combinedRealArgs]);
2447+
}
2448+
2449+
JS.Expression _getSuperHelperFor(String name, JS.Expression forwardedCall,
2450+
List<JS.Expression> helperArgs) {
2451+
var helperMethod =
2452+
new JS.Fun(helperArgs, new JS.Block([new JS.Return(forwardedCall)]));
2453+
var helperMethodName = new JS.TemporaryId('super\$$name');
2454+
_superHelperSymbols.add(helperMethodName);
2455+
_superHelpers.add(new JS.Method(helperMethodName, helperMethod));
2456+
return helperMethodName;
2457+
}
2458+
2459+
/// Emits a (possibly generic) instance method call.
2460+
JS.Expression _emitMethodCallInternal(
2461+
Expression target,
2462+
MethodInvocation node,
2463+
List<JS.Expression> args,
2464+
List<JS.Expression> typeArgs) {
23892465
var type = getStaticType(target);
23902466
var name = node.methodName.name;
23912467
var element = node.methodName.staticElement;
23922468
bool isStatic = element is ExecutableElement && element.isStatic;
23932469
var memberName = _emitMemberName(name, type: type, isStatic: isStatic);
23942470

23952471
JS.Expression jsTarget = _visit(target);
2396-
var typeArgs = _emitInvokeTypeArguments(node);
2397-
List<JS.Expression> args = _visit(node.argumentList);
23982472
if (DynamicInvoke.get(target)) {
23992473
if (typeArgs != null) {
24002474
return js.call('dart.dgsend(#, #, #, #)',
@@ -2410,6 +2484,7 @@ class CodeGenerator extends GeneralizingAstVisitor
24102484
}
24112485

24122486
jsTarget = new JS.PropertyAccess(jsTarget, memberName);
2487+
24132488
if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs);
24142489

24152490
if (DynamicInvoke.get(node.methodName)) {
@@ -3544,8 +3619,45 @@ class CodeGenerator extends GeneralizingAstVisitor
35443619
if (member is PropertyAccessorElement) {
35453620
member = (member as PropertyAccessorElement).variable;
35463621
}
3622+
String memberName = memberId.name;
3623+
var typeArgs = _getTypeArgs(member, resultType);
3624+
3625+
if (target is SuperExpression && !_superAllowed) {
3626+
return _emitSuperHelperAccess(target, member, memberName, typeArgs);
3627+
}
3628+
return _emitAccessInternal(target, member, memberName, typeArgs);
3629+
}
3630+
3631+
JS.Expression _emitSuperHelperAccess(SuperExpression target, Element member,
3632+
String memberName, List<JS.Expression> typeArgs) {
3633+
var fakeTypeArgs =
3634+
typeArgs?.map((_) => new JS.TemporaryId('a'))?.toList(growable: false);
3635+
3636+
var forwardedAccess =
3637+
_emitAccessInternal(target, member, memberName, fakeTypeArgs);
3638+
var superForwarder = _getSuperHelperFor(
3639+
memberName, forwardedAccess, fakeTypeArgs ?? const []);
3640+
3641+
return js.call('this.#(#)', [superForwarder, typeArgs ?? const []]);
3642+
}
3643+
3644+
List<JS.Expression> _getTypeArgs(Element member, DartType instantiated) {
3645+
DartType type;
3646+
if (member is ExecutableElement) {
3647+
type = member.type;
3648+
} else if (member is VariableElement) {
3649+
type = member.type;
3650+
}
3651+
3652+
// TODO(jmesserly): handle explicitly passed type args.
3653+
if (type == null) return null;
3654+
return _emitFunctionTypeArguments(type, instantiated);
3655+
}
3656+
3657+
JS.Expression _emitAccessInternal(Expression target, Element member,
3658+
String memberName, List<JS.Expression> typeArgs) {
35473659
bool isStatic = member is ClassMemberElement && member.isStatic;
3548-
var name = _emitMemberName(memberId.name,
3660+
var name = _emitMemberName(memberName,
35493661
type: getStaticType(target), isStatic: isStatic);
35503662
if (DynamicInvoke.get(target)) {
35513663
return js.call('dart.dload(#, #)', [_visit(target), name]);
@@ -3565,34 +3677,19 @@ class CodeGenerator extends GeneralizingAstVisitor
35653677
// Tear-off methods: explicitly bind it.
35663678
if (isSuper) {
35673679
result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]);
3568-
} else if (_isObjectMemberCall(target, memberId.name)) {
3680+
} else if (_isObjectMemberCall(target, memberName)) {
35693681
result = js.call('dart.bind(#, #, dart.#)', [jsTarget, name, name]);
35703682
} else {
35713683
result = js.call('dart.bind(#, #)', [jsTarget, name]);
35723684
}
3573-
} else if (_isObjectMemberCall(target, memberId.name)) {
3685+
} else if (_isObjectMemberCall(target, memberName)) {
35743686
result = js.call('dart.#(#)', [name, jsTarget]);
35753687
} else {
35763688
result = js.call('#.#', [jsTarget, name]);
35773689
}
3578-
return _applyFunctionTypeArguments(result, member, resultType);
3579-
}
3580-
3581-
/// If this is an inferred instantiation of a generic function/method, this
3582-
/// will add the inferred type arguments.
3583-
JS.Expression _applyFunctionTypeArguments(
3584-
JS.Expression result, Element member, DartType instantiated) {
3585-
DartType type;
3586-
if (member is ExecutableElement) {
3587-
type = member.type;
3588-
} else if (member is VariableElement) {
3589-
type = member.type;
3690+
if (typeArgs == null) {
3691+
return result;
35903692
}
3591-
3592-
// TODO(jmesserly): handle explicitly passed type args.
3593-
if (type == null) return result;
3594-
var typeArgs = _emitFunctionTypeArguments(type, instantiated);
3595-
if (typeArgs == null) return result;
35963693
return js.call('dart.gbind(#, #)', [result, typeArgs]);
35973694
}
35983695

@@ -3790,8 +3887,12 @@ class CodeGenerator extends GeneralizingAstVisitor
37903887

37913888
@override
37923889
visitTryStatement(TryStatement node) {
3793-
return new JS.Try(_visit(node.body), _visitCatch(node.catchClauses),
3794-
_visit(node.finallyBlock));
3890+
var savedSuperAllowed = _superAllowed;
3891+
_superAllowed = false;
3892+
var finallyBlock = _visit(node.finallyBlock);
3893+
_superAllowed = savedSuperAllowed;
3894+
return new JS.Try(
3895+
_visit(node.body), _visitCatch(node.catchClauses), finallyBlock);
37953896
}
37963897

37973898
_visitCatch(NodeList<CatchClause> clauses) {

pkg/dev_compiler/lib/src/compiler/source_map_printer.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2-
32
// for details. All rights reserved. Use of this source code is governed by a
43
// BSD-style license that can be found in the LICENSE file.
54

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import "package:expect/expect.dart";
6+
7+
import 'dart:async';
8+
9+
class A {
10+
Future<int> foo() async => 42;
11+
}
12+
13+
class B extends A {
14+
Future<int> foo() async {
15+
var x = await super.foo();
16+
return x + 1;
17+
}
18+
}
19+
20+
main() async {
21+
Expect.equals(43, await new B().foo());
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import "package:expect/expect.dart";
6+
7+
import 'dart:async';
8+
9+
class A {
10+
Future<int> foo() async => 42;
11+
}
12+
13+
class B extends A {
14+
Future<int> foo() async {
15+
var x = await super.foo();
16+
var y = await super.foo();
17+
return x + y;
18+
}
19+
}
20+
21+
main() async {
22+
Expect.equals(84, await new B().foo());
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import "package:expect/expect.dart";
6+
7+
import 'dart:async';
8+
9+
class A {
10+
Future/*<T>*/ foo/*<T>*/(/*=T*/ x) async => x;
11+
}
12+
13+
class B extends A {
14+
Future<int> bar() async {
15+
var x = await super.foo(41);
16+
return x + 1;
17+
}
18+
}
19+
20+
main() async {
21+
Expect.equals(42, await new B().bar());
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import "package:expect/expect.dart";
6+
7+
import 'dart:async';
8+
9+
class A {
10+
Future/*<T>*/ foo/*<T>*/({/*=T*/ x}) async => x;
11+
}
12+
13+
class B extends A {
14+
Future<int> bar() async {
15+
var x = await super.foo(x: 41);
16+
return x + 1;
17+
}
18+
}
19+
20+
main() async {
21+
Expect.equals(42, await new B().bar());
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import "package:expect/expect.dart";
6+
7+
import 'dart:async';
8+
9+
class A {
10+
Future<int> get foo async => 42;
11+
}
12+
13+
class B extends A {
14+
Future<int> bar() async {
15+
var x = await super.foo;
16+
return x + 1;
17+
}
18+
}
19+
20+
main() async {
21+
Expect.equals(43, await new B().bar());
22+
}

0 commit comments

Comments
 (0)