diff --git a/pkg/dev_compiler/lib/runtime/dart_sdk.js b/pkg/dev_compiler/lib/runtime/dart_sdk.js index 1b3ffa6d399f..deccf92d9908 100644 --- a/pkg/dev_compiler/lib/runtime/dart_sdk.js +++ b/pkg/dev_compiler/lib/runtime/dart_sdk.js @@ -25019,8 +25019,8 @@ dart_library.library('dart_sdk', null, /* Imports */[ return _ConverterStreamEventSink; }); convert._ConverterStreamEventSink = convert._ConverterStreamEventSink$(); - const _first$ = Symbol('_first'); const _second = Symbol('_second'); + const _first$ = Symbol('_first'); convert._FusedCodec$ = dart.generic((S, M, T) => { class _FusedCodec extends convert.Codec$(S, T) { get encoder() { diff --git a/pkg/dev_compiler/lib/src/compiler/code_generator.dart b/pkg/dev_compiler/lib/src/compiler/code_generator.dart index 255f15931c42..73c4eb0fadac 100644 --- a/pkg/dev_compiler/lib/src/compiler/code_generator.dart +++ b/pkg/dev_compiler/lib/src/compiler/code_generator.dart @@ -118,6 +118,11 @@ class CodeGenerator extends GeneralizingAstVisitor String _buildRoot; + bool _superAllowed = true; + + List _superHelperSymbols = []; + List _superHelpers = []; + /// Whether we are currently generating code for the body of a `JS()` call. bool _isInForeignJS = false; @@ -595,6 +600,7 @@ class CodeGenerator extends GeneralizingAstVisitor var body = []; var extensions = _extensionsToImplement(classElem); _initExtensionSymbols(classElem, methods, fields, body); + _emitSuperHelperSymbols(_superHelperSymbols, body); // Emit the class, e.g. `core.Object = class Object { ... }` _defineClass(classElem, className, classExpr, body); @@ -619,6 +625,14 @@ class CodeGenerator extends GeneralizingAstVisitor return _statement(body); } + void _emitSuperHelperSymbols( + List superHelperSymbols, List body) { + for (var id in superHelperSymbols) { + body.add(js.statement('const # = Symbol(#)', [id, js.string(id.name)])); + } + superHelperSymbols.clear(); + } + void _registerPropertyOverrides( ClassElement classElem, JS.Expression className, @@ -897,6 +911,10 @@ class CodeGenerator extends GeneralizingAstVisitor jsMethods.add(_emitIterable(type)); } + // Add all of the super helper methods + jsMethods.addAll(_superHelpers); + _superHelpers.clear(); + return jsMethods.where((m) => m != null).toList(growable: false); } @@ -1630,7 +1648,7 @@ class CodeGenerator extends GeneralizingAstVisitor } } - JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { + JS.Method _emitMethodDeclaration(InterfaceType type, MethodDeclaration node) { if (node.isAbstract) { return null; } @@ -1956,8 +1974,11 @@ class CodeGenerator extends GeneralizingAstVisitor } else { _asyncStarController = null; } + var savedSuperAllowed = _superAllowed; + _superAllowed = false; // Visit the body with our async* controller set. var jsBody = _visit(body); + _superAllowed = savedSuperAllowed; _asyncStarController = savedController; DartType returnType = _getExpectedReturnType(element); @@ -2003,8 +2024,12 @@ class CodeGenerator extends GeneralizingAstVisitor /// function instantiation. @override JS.Expression visitSimpleIdentifier(SimpleIdentifier node) { - return _applyFunctionTypeArguments( - _emitSimpleIdentifier(node), node.staticElement, node.staticType); + var typeArgs = _getTypeArgs(node.staticElement, node.staticType); + var simpleId = _emitSimpleIdentifier(node); + if (typeArgs == null) { + return simpleId; + } + return js.call('dart.gbind(#, #)', [simpleId, typeArgs]); } /// Emits a simple identifier, handling implicit `this` as well as @@ -2384,8 +2409,59 @@ class CodeGenerator extends GeneralizingAstVisitor return _emitMethodCall(target, node); } - /// Emits a (possibly generic) instance method call. JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { + List args = _visit(node.argumentList); + var typeArgs = _emitInvokeTypeArguments(node); + + if (target is SuperExpression && !_superAllowed) { + return _emitSuperHelperCall(typeArgs, args, target, node); + } + + return _emitMethodCallInternal(target, node, args, typeArgs); + } + + JS.Expression _emitSuperHelperCall(List typeArgs, + List args, SuperExpression target, MethodInvocation node) { + var fakeTypeArgs = + typeArgs?.map((_) => new JS.TemporaryId('a'))?.toList(growable: false); + var fakeArgs = + args.map((_) => new JS.TemporaryId('a')).toList(growable: false); + var combinedFakeArgs = []; + if (fakeTypeArgs != null) { + combinedFakeArgs.addAll(fakeTypeArgs); + } + combinedFakeArgs.addAll(fakeArgs); + + var forwardedCall = + _emitMethodCallInternal(target, node, fakeArgs, fakeTypeArgs); + var superForwarder = _getSuperHelperFor( + node.methodName.name, forwardedCall, combinedFakeArgs); + + var combinedRealArgs = []; + if (typeArgs != null) { + combinedRealArgs.addAll(typeArgs); + } + combinedRealArgs.addAll(args); + + return js.call('this.#(#)', [superForwarder, combinedRealArgs]); + } + + JS.Expression _getSuperHelperFor(String name, JS.Expression forwardedCall, + List helperArgs) { + var helperMethod = + new JS.Fun(helperArgs, new JS.Block([new JS.Return(forwardedCall)])); + var helperMethodName = new JS.TemporaryId('super\$$name'); + _superHelperSymbols.add(helperMethodName); + _superHelpers.add(new JS.Method(helperMethodName, helperMethod)); + return helperMethodName; + } + + /// Emits a (possibly generic) instance method call. + JS.Expression _emitMethodCallInternal( + Expression target, + MethodInvocation node, + List args, + List typeArgs) { var type = getStaticType(target); var name = node.methodName.name; var element = node.methodName.staticElement; @@ -2393,8 +2469,6 @@ class CodeGenerator extends GeneralizingAstVisitor var memberName = _emitMemberName(name, type: type, isStatic: isStatic); JS.Expression jsTarget = _visit(target); - var typeArgs = _emitInvokeTypeArguments(node); - List args = _visit(node.argumentList); if (DynamicInvoke.get(target)) { if (typeArgs != null) { return js.call('dart.dgsend(#, #, #, #)', @@ -2410,6 +2484,7 @@ class CodeGenerator extends GeneralizingAstVisitor } jsTarget = new JS.PropertyAccess(jsTarget, memberName); + if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); if (DynamicInvoke.get(node.methodName)) { @@ -3544,8 +3619,45 @@ class CodeGenerator extends GeneralizingAstVisitor if (member is PropertyAccessorElement) { member = (member as PropertyAccessorElement).variable; } + String memberName = memberId.name; + var typeArgs = _getTypeArgs(member, resultType); + + if (target is SuperExpression && !_superAllowed) { + return _emitSuperHelperAccess(target, member, memberName, typeArgs); + } + return _emitAccessInternal(target, member, memberName, typeArgs); + } + + JS.Expression _emitSuperHelperAccess(SuperExpression target, Element member, + String memberName, List typeArgs) { + var fakeTypeArgs = + typeArgs?.map((_) => new JS.TemporaryId('a'))?.toList(growable: false); + + var forwardedAccess = + _emitAccessInternal(target, member, memberName, fakeTypeArgs); + var superForwarder = _getSuperHelperFor( + memberName, forwardedAccess, fakeTypeArgs ?? const []); + + return js.call('this.#(#)', [superForwarder, typeArgs ?? const []]); + } + + List _getTypeArgs(Element member, DartType instantiated) { + DartType type; + if (member is ExecutableElement) { + type = member.type; + } else if (member is VariableElement) { + type = member.type; + } + + // TODO(jmesserly): handle explicitly passed type args. + if (type == null) return null; + return _emitFunctionTypeArguments(type, instantiated); + } + + JS.Expression _emitAccessInternal(Expression target, Element member, + String memberName, List typeArgs) { bool isStatic = member is ClassMemberElement && member.isStatic; - var name = _emitMemberName(memberId.name, + var name = _emitMemberName(memberName, type: getStaticType(target), isStatic: isStatic); if (DynamicInvoke.get(target)) { return js.call('dart.dload(#, #)', [_visit(target), name]); @@ -3565,34 +3677,19 @@ class CodeGenerator extends GeneralizingAstVisitor // Tear-off methods: explicitly bind it. if (isSuper) { result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]); - } else if (_isObjectMemberCall(target, memberId.name)) { + } else if (_isObjectMemberCall(target, memberName)) { result = js.call('dart.bind(#, #, dart.#)', [jsTarget, name, name]); } else { result = js.call('dart.bind(#, #)', [jsTarget, name]); } - } else if (_isObjectMemberCall(target, memberId.name)) { + } else if (_isObjectMemberCall(target, memberName)) { result = js.call('dart.#(#)', [name, jsTarget]); } else { result = js.call('#.#', [jsTarget, name]); } - return _applyFunctionTypeArguments(result, member, resultType); - } - - /// If this is an inferred instantiation of a generic function/method, this - /// will add the inferred type arguments. - JS.Expression _applyFunctionTypeArguments( - JS.Expression result, Element member, DartType instantiated) { - DartType type; - if (member is ExecutableElement) { - type = member.type; - } else if (member is VariableElement) { - type = member.type; + if (typeArgs == null) { + return result; } - - // TODO(jmesserly): handle explicitly passed type args. - if (type == null) return result; - var typeArgs = _emitFunctionTypeArguments(type, instantiated); - if (typeArgs == null) return result; return js.call('dart.gbind(#, #)', [result, typeArgs]); } @@ -3790,8 +3887,12 @@ class CodeGenerator extends GeneralizingAstVisitor @override visitTryStatement(TryStatement node) { - return new JS.Try(_visit(node.body), _visitCatch(node.catchClauses), - _visit(node.finallyBlock)); + var savedSuperAllowed = _superAllowed; + _superAllowed = false; + var finallyBlock = _visit(node.finallyBlock); + _superAllowed = savedSuperAllowed; + return new JS.Try( + _visit(node.body), _visitCatch(node.catchClauses), finallyBlock); } _visitCatch(NodeList clauses) { diff --git a/pkg/dev_compiler/lib/src/compiler/source_map_printer.dart b/pkg/dev_compiler/lib/src/compiler/source_map_printer.dart index dc289930e27b..3a12972bec70 100644 --- a/pkg/dev_compiler/lib/src/compiler/source_map_printer.dart +++ b/pkg/dev_compiler/lib/src/compiler/source_map_printer.dart @@ -1,5 +1,4 @@ // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file - // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. diff --git a/pkg/dev_compiler/test/codegen/language/super_in_async1_test.dart b/pkg/dev_compiler/test/codegen/language/super_in_async1_test.dart new file mode 100644 index 000000000000..d0bc61ad93cb --- /dev/null +++ b/pkg/dev_compiler/test/codegen/language/super_in_async1_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "package:expect/expect.dart"; + +import 'dart:async'; + +class A { + Future foo() async => 42; +} + +class B extends A { + Future foo() async { + var x = await super.foo(); + return x + 1; + } +} + +main() async { + Expect.equals(43, await new B().foo()); +} \ No newline at end of file diff --git a/pkg/dev_compiler/test/codegen/language/super_in_async2_test.dart b/pkg/dev_compiler/test/codegen/language/super_in_async2_test.dart new file mode 100644 index 000000000000..403a19431922 --- /dev/null +++ b/pkg/dev_compiler/test/codegen/language/super_in_async2_test.dart @@ -0,0 +1,23 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "package:expect/expect.dart"; + +import 'dart:async'; + +class A { + Future foo() async => 42; +} + +class B extends A { + Future foo() async { + var x = await super.foo(); + var y = await super.foo(); + return x + y; + } +} + +main() async { + Expect.equals(84, await new B().foo()); +} \ No newline at end of file diff --git a/pkg/dev_compiler/test/codegen/language/super_in_async3_test.dart b/pkg/dev_compiler/test/codegen/language/super_in_async3_test.dart new file mode 100644 index 000000000000..9bab80fc33eb --- /dev/null +++ b/pkg/dev_compiler/test/codegen/language/super_in_async3_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "package:expect/expect.dart"; + +import 'dart:async'; + +class A { + Future/**/ foo/**/(/*=T*/ x) async => x; +} + +class B extends A { + Future bar() async { + var x = await super.foo(41); + return x + 1; + } +} + +main() async { + Expect.equals(42, await new B().bar()); +} \ No newline at end of file diff --git a/pkg/dev_compiler/test/codegen/language/super_in_async4_test.dart b/pkg/dev_compiler/test/codegen/language/super_in_async4_test.dart new file mode 100644 index 000000000000..488502b4dc08 --- /dev/null +++ b/pkg/dev_compiler/test/codegen/language/super_in_async4_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "package:expect/expect.dart"; + +import 'dart:async'; + +class A { + Future/**/ foo/**/({/*=T*/ x}) async => x; +} + +class B extends A { + Future bar() async { + var x = await super.foo(x: 41); + return x + 1; + } +} + +main() async { + Expect.equals(42, await new B().bar()); +} \ No newline at end of file diff --git a/pkg/dev_compiler/test/codegen/language/super_in_async5_test.dart b/pkg/dev_compiler/test/codegen/language/super_in_async5_test.dart new file mode 100644 index 000000000000..3b42ba6beb0a --- /dev/null +++ b/pkg/dev_compiler/test/codegen/language/super_in_async5_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "package:expect/expect.dart"; + +import 'dart:async'; + +class A { + Future get foo async => 42; +} + +class B extends A { + Future bar() async { + var x = await super.foo; + return x + 1; + } +} + +main() async { + Expect.equals(43, await new B().bar()); +} \ No newline at end of file diff --git a/pkg/dev_compiler/test/codegen/language/super_in_async6_test.dart b/pkg/dev_compiler/test/codegen/language/super_in_async6_test.dart new file mode 100644 index 000000000000..b07864d2e8ba --- /dev/null +++ b/pkg/dev_compiler/test/codegen/language/super_in_async6_test.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "package:expect/expect.dart"; + +import 'dart:async'; + +class A { + Future foo(int x, int y, int z) async => x + y + z; +} + +class B extends A { + Future foo(int x, int y, int z) async { + var w = await super.foo(x, y, z); + return w + 1; + } +} + +main() async { + Expect.equals(7, await new B().foo(1, 2, 3)); +} \ No newline at end of file diff --git a/pkg/dev_compiler/test/codegen/language/super_in_finally_test.dart b/pkg/dev_compiler/test/codegen/language/super_in_finally_test.dart new file mode 100644 index 000000000000..0e8286267ecb --- /dev/null +++ b/pkg/dev_compiler/test/codegen/language/super_in_finally_test.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "package:expect/expect.dart"; + +class A { + /**/ foo/**/({/*=T*/ x}) => x; +} + +class B extends A { + int bar() { + try { + throw 'bar'; + return 1; + } finally { + var x = super.foo(x: 41); + return x + 1; + } + } +} + +main() { + Expect.equals(42, new B().bar()); +} \ No newline at end of file