[dart2wasm] Make use of TFA-inferred types in more cases
Avoid falling back to variable type if the variable is `final` and has a
more precise inferred type.
Use unboxing information on methods to make unboxed return types
when TFA concludes it's int/double.
This will partially fix a performance regression in SplayHarder
benchmark where a parameter of type `num` was unboxed to `double` but
the local variable was kept as `num` and therefore always caused a box
allocation. After this change the box allocation is moved to the place
where we call `double.toString()`.
TEST=Updated expectation files.
Change-Id: I0252cf86ada9a8affbb46a31e913504930057650
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/365560
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/dart2wasm/lib/code_generator.dart b/pkg/dart2wasm/lib/code_generator.dart
index 7a52892..5584f31 100644
--- a/pkg/dart2wasm/lib/code_generator.dart
+++ b/pkg/dart2wasm/lib/code_generator.dart
@@ -385,7 +385,7 @@
);
}
}
- if (!isForwarder) {
+ if (!isForwarder && !variable.isFinal) {
// We now have a precise local that can contain the values passed by
// callers, but the body may assign less precise types to this variable,
// so we may introduce another local variable that is less precise.
diff --git a/pkg/dart2wasm/lib/dispatch_table.dart b/pkg/dart2wasm/lib/dispatch_table.dart
index bfea39e..2cfb1ea 100644
--- a/pkg/dart2wasm/lib/dispatch_table.dart
+++ b/pkg/dart2wasm/lib/dispatch_table.dart
@@ -101,7 +101,7 @@
if (target.isImplicitGetter) {
positional = const [];
named = const {};
- returns = [member.getterType];
+ returns = [translator.typeOfReturnValue(member)];
} else {
positional = [member.setterType];
named = const {};
@@ -124,7 +124,9 @@
for (VariableDeclaration param in function.namedParameters)
param.name!: typeForParam(param, param.isRequired)
};
- returns = target.isSetter ? const [] : [function.returnType];
+ returns = target.isSetter
+ ? const []
+ : [translator.typeOfReturnValue(member)];
}
}
assert(returns.length <= outputSets.length);
diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart
index 7876d9c..aa49338 100644
--- a/pkg/dart2wasm/lib/functions.dart
+++ b/pkg/dart2wasm/lib/functions.dart
@@ -64,7 +64,7 @@
// `WebAssembly.Function`.
m.types.splitRecursionGroup();
w.FunctionType ftype = _makeFunctionType(
- translator, member.reference, [member.function.returnType], null,
+ translator, member.reference, null,
isImportOrExport: true);
m.types.splitRecursionGroup();
_functions[member.reference] =
@@ -82,8 +82,7 @@
// publicly exposed types to be defined in separate recursion groups
// from GC types.
m.types.splitRecursionGroup();
- _makeFunctionType(
- translator, member.reference, [member.function.returnType], null,
+ _makeFunctionType(translator, member.reference, null,
isImportOrExport: true);
m.types.splitRecursionGroup();
}
@@ -106,9 +105,8 @@
_worklist.add(target);
assert(!node.isInstanceMember);
assert(!node.isGetter);
- w.FunctionType ftype = _makeFunctionType(
- translator, target, [node.function.returnType], null,
- isImportOrExport: true);
+ w.FunctionType ftype =
+ _makeFunctionType(translator, target, null, isImportOrExport: true);
w.BaseFunction function = m.functions.define(ftype, "$node");
_functions[target] = function;
m.exports.export(export.value, function);
@@ -226,7 +224,7 @@
if (!node.isInstanceMember) {
if (target == node.fieldReference) {
// Static field initializer function
- return _makeFunctionType(translator, target, [node.type], null);
+ return _makeFunctionType(translator, target, null);
}
String kind = target == node.setterReference ? "setter" : "getter";
throw "No implicit $kind function for static field: $node";
@@ -239,8 +237,7 @@
assert(!node.isAbstract);
return node.isInstanceMember
? translator.dispatchTable.selectorForTarget(node.reference).signature
- : _makeFunctionType(
- translator, target, [node.function.returnType], null);
+ : _makeFunctionType(translator, target, null);
}
@override
@@ -440,8 +437,8 @@
return inputs;
}
-w.FunctionType _makeFunctionType(Translator translator, Reference target,
- List<DartType> returnTypes, w.ValueType? receiverType,
+w.FunctionType _makeFunctionType(
+ Translator translator, Reference target, w.ValueType? receiverType,
{bool isImportOrExport = false}) {
Member member = target.asMember;
@@ -461,12 +458,13 @@
(isImportOrExport && t is VoidType) ||
(t is InterfaceType && t.classNode == translator.wasmVoidClass);
- final List<w.ValueType> outputs = emptyOutputList
- ? const []
- : returnTypes
- .where((t) => !isVoidType(t))
- .map((t) => translateType(t))
- .toList();
+ final List<w.ValueType> outputs;
+ if (emptyOutputList) {
+ outputs = const [];
+ } else {
+ final DartType returnType = translator.typeOfReturnValue(member);
+ outputs = !isVoidType(returnType) ? [translateType(returnType)] : const [];
+ }
return translator.m.types.defineFunction(inputs, outputs);
}
diff --git a/pkg/dart2wasm/lib/transformers.dart b/pkg/dart2wasm/lib/transformers.dart
index 4f8471e..ec82b67 100644
--- a/pkg/dart2wasm/lib/transformers.dart
+++ b/pkg/dart2wasm/lib/transformers.dart
@@ -32,6 +32,7 @@
Member? _currentMember;
StaticTypeContext? _cachedTypeContext;
+ final Set<VariableDeclaration> _implicitFinalVariables = {};
final Library _coreLibrary;
final InterfaceType _nonNullableTypeType;
@@ -112,9 +113,13 @@
defaultMember(Member node) {
_currentMember = node;
_cachedTypeContext = null;
+ _implicitFinalVariables.clear();
final result = super.defaultMember(node);
+ for (final node in _implicitFinalVariables) {
+ node.isFinal = true;
+ }
_currentMember = null;
_cachedTypeContext = null;
return result;
@@ -153,6 +158,20 @@
}
@override
+ visitVariableDeclaration(VariableDeclaration node) {
+ if (!node.isFinal) {
+ _implicitFinalVariables.add(node);
+ }
+ return super.visitVariableDeclaration(node);
+ }
+
+ @override
+ visitVariableSet(VariableSet node) {
+ _implicitFinalVariables.remove(node.variable);
+ return super.visitVariableSet(node);
+ }
+
+ @override
TreeNode visitClass(Class cls) {
// For every concrete class whose type parameters do not match the type
// parameters of it's super class we embed a special virtual function
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index de62490..7d4f330 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -11,6 +11,7 @@
import 'package:kernel/type_environment.dart';
import 'package:vm/metadata/direct_call.dart';
import 'package:vm/metadata/inferred_type.dart';
+import 'package:vm/metadata/unboxing_info.dart';
import 'package:wasm_builder/wasm_builder.dart' as w;
import 'class_info.dart';
@@ -79,6 +80,10 @@
(component.metadata[InferredArgTypeMetadataRepository.repositoryTag]
as InferredArgTypeMetadataRepository)
.mapping;
+ late final Map<TreeNode, UnboxingInfoMetadata> unboxingInfoMetadata =
+ (component.metadata[UnboxingInfoMetadataRepository.repositoryTag]
+ as UnboxingInfoMetadataRepository)
+ .mapping;
// Other parts of the global compiler state.
@override
@@ -859,11 +864,10 @@
return;
}
if (to != voidMarker) {
- assert(to is w.RefType && to.nullable);
- // This can happen when a void method has its return type overridden
- // to return a value, in which case the selector signature will have a
- // non-void return type to encompass all possible return values.
- b.ref_null((to as w.RefType).heapType.bottomType);
+ // This can happen e.g. when a `return;` is guaranteed to be never taken
+ // but TFA didn't remove the dead code. In that case we synthesize a
+ // dummy value.
+ globals.instantiateDummyValue(b, to);
return;
}
}
@@ -990,11 +994,30 @@
return node.type;
}
+ DartType typeOfReturnValue(Member member) {
+ final unboxingInfo = unboxingInfoMetadata[member];
+ if (unboxingInfo != null) {
+ final returnInfo = unboxingInfo.returnInfo;
+ if (returnInfo.kind == UnboxingKind.int) {
+ return coreTypes.intRawType(Nullability.nonNullable);
+ }
+ if (returnInfo.kind == UnboxingKind.double) {
+ return coreTypes.doubleRawType(Nullability.nonNullable);
+ }
+ }
+ if (member is Field) return member.type;
+ return member.function!.returnType;
+ }
+
w.ValueType translateTypeOfParameter(
VariableDeclaration node, bool isRequired) {
return translateType(typeOfParameterVariable(node, isRequired));
}
+ w.ValueType translateTypeOfReturnValue(Member node) {
+ return translateType(typeOfReturnValue(node));
+ }
+
w.ValueType translateTypeOfField(Field node) {
return translateType(_inferredTypeOfField(node) ?? node.type);
}
@@ -1012,7 +1035,11 @@
}
DartType? _inferredTypeOfLocalVariable(VariableDeclaration node) {
- return _filterInferredType(node.type, inferredTypeMetadata[node]);
+ InferredType? inferredType = inferredTypeMetadata[node];
+ if (node.isFinal) {
+ inferredType ??= inferredTypeMetadata[node.initializer];
+ }
+ return _filterInferredType(node.type, inferredType);
}
DartType? _filterInferredType(
diff --git a/pkg/front_end/testcases/dart2wasm/for_in.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2wasm/for_in.dart.strong.transformed.expect
index 6aafc34..33f238d 100644
--- a/pkg/front_end/testcases/dart2wasm/for_in.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2wasm/for_in.dart.strong.transformed.expect
@@ -3,22 +3,22 @@
import "dart:core" as core;
import "dart:async" as asy;
-static method method(core::Iterable<core::int> iterable) → dynamic {
+static method method(final core::Iterable<core::int> iterable) → dynamic {
{
- synthesized core::Iterator<core::int> #forIterator = iterable.{core::Iterable::iterator}{core::Iterator<core::int>};
+ final synthesized core::Iterator<core::int> #forIterator = iterable.{core::Iterable::iterator}{core::Iterator<core::int>};
for (; #forIterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
- core::int i = #forIterator.{core::Iterator::current}{core::int};
+ final core::int i = #forIterator.{core::Iterator::current}{core::int};
{
core::print(i);
}
}
}
}
-static method asyncMethod(asy::Stream<core::int> stream) → dynamic async /* emittedValueType= dynamic */ {
+static method asyncMethod(final asy::Stream<core::int> stream) → dynamic async /* emittedValueType= dynamic */ {
core::bool :async_temporary_0;
dynamic :async_temporary_1;
{
- synthesized asy::_StreamIterator<core::int> #forIterator = new asy::_StreamIterator::•<core::int>(stream);
+ final synthesized asy::_StreamIterator<core::int> #forIterator = new asy::_StreamIterator::•<core::int>(stream);
synthesized core::bool #jumpSentinel = #C1;
{
core::int #t1 = 0;
@@ -29,7 +29,7 @@
for (; ; ) {
:async_temporary_0 = await #forIterator.{asy::_StreamIterator::moveNext}(){() → asy::Future<core::bool>};
if(#jumpSentinel = :async_temporary_0 as dynamic) {
- core::int i = #forIterator.{asy::_StreamIterator::current}{core::int};
+ final core::int i = #forIterator.{asy::_StreamIterator::current}{core::int};
{
core::print(i);
}
diff --git a/pkg/front_end/testcases/dart2wasm/inference_update_2/issue52452.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2wasm/inference_update_2/issue52452.dart.strong.transformed.expect
index 8417c49..52ea82d 100644
--- a/pkg/front_end/testcases/dart2wasm/inference_update_2/issue52452.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2wasm/inference_update_2/issue52452.dart.strong.transformed.expect
@@ -7,29 +7,29 @@
final field core::int? _f2;
final field core::int? _f3;
final field core::int? _f4;
- constructor •(core::int? i) → self::C
+ constructor •(final core::int? i) → self::C
: self::C::_f2 = i, self::C::_f3 = i, self::C::_f4 = i, super core::Object::•()
;
- static method _#new#tearOff(core::int? i) → self::C
+ static method _#new#tearOff(final core::int? i) → self::C
return new self::C::•(i);
}
class A extends core::Object {
- constructor •(core::int? i) → self::A
+ constructor •(final core::int? i) → self::A
: super core::Object::•()
;
- static method _#new#tearOff(core::int? i) → self::A
+ static method _#new#tearOff(final core::int? i) → self::A
return new self::A::•(i);
}
abstract class M3 extends core::Object /*isMixinDeclaration*/ {
}
abstract class _D&A&M3 extends self::A implements self::M3 /*isAnonymousMixin,isEliminatedMixin*/ {
- synthetic constructor •(core::int? i) → self::_D&A&M3
+ synthetic constructor •(final core::int? i) → self::_D&A&M3
: super self::A::•(i)
;
}
abstract class D extends self::_D&A&M3 {
final field core::int? _f4;
- constructor •(core::int? i) → self::D
+ constructor •(final core::int? i) → self::D
: self::D::_f4 = i, super self::_D&A&M3::•(i)
;
}
@@ -42,13 +42,13 @@
field core::bool _#M1#_f4#isSet = false;
get _f2() → core::int?
return this.{self::M1::_#M1#_f2#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f2}{core::int?} : throw{for-error-handling} new _in::LateError::fieldNI("_f2");
- set _f2(core::int? _f2#param) → void {
+ set _f2(final core::int? _f2#param) → void {
this.{self::M1::_#M1#_f2#isSet} = true;
this.{self::M1::_#M1#_f2} = _f2#param;
}
get _f3() → core::int?
return this.{self::M1::_#M1#_f3#isSet}{core::bool} ?{core::int?} this.{self::M1::_#M1#_f3}{core::int?} : throw{for-error-handling} new _in::LateError::fieldNI("_f3");
- set _f3(core::int? _f3#param) → void
+ set _f3(final core::int? _f3#param) → void
if(this.{self::M1::_#M1#_f3#isSet}{core::bool})
throw{for-error-handling} new _in::LateError::fieldAI("_f3");
else {
@@ -67,10 +67,10 @@
}
}
class B extends core::Object {
- constructor •(core::int? i) → self::B
+ constructor •(final core::int? i) → self::B
: super core::Object::•()
;
- static method _#new#tearOff(core::int? i) → self::B
+ static method _#new#tearOff(final core::int? i) → self::B
return new self::B::•(i);
}
abstract class _E&B&M1 extends self::B implements self::M1 /*isAnonymousMixin,isEliminatedMixin*/ {
@@ -80,7 +80,7 @@
field core::bool _#M1#_f3#isSet = false;
field core::int? _#M1#_f4 = null;
field core::bool _#M1#_f4#isSet = false;
- synthetic constructor •(core::int? i) → self::_E&B&M1
+ synthetic constructor •(final core::int? i) → self::_E&B&M1
: super self::B::•(i)
;
get _f2() → core::int?
@@ -97,11 +97,11 @@
}
return this.{self::M1::_#M1#_f4}{core::int?};
}
- set _f2(core::int? _f2#param) → void {
+ set _f2(final core::int? _f2#param) → void {
this.{self::M1::_#M1#_f2#isSet} = true;
this.{self::M1::_#M1#_f2} = _f2#param;
}
- set _f3(core::int? _f3#param) → void
+ set _f3(final core::int? _f3#param) → void
if(this.{self::M1::_#M1#_f3#isSet}{core::bool})
throw{for-error-handling} new _in::LateError::fieldAI("_f3");
else {
@@ -110,39 +110,39 @@
}
}
class E extends self::_E&B&M1 implements self::D {
- constructor •(core::int? i) → self::E
+ constructor •(final core::int? i) → self::E
: super self::_E&B&M1::•(i)
;
- static method _#new#tearOff(core::int? i) → self::E
+ static method _#new#tearOff(final core::int? i) → self::E
return new self::E::•(i);
@#C1
- method noSuchMethod(core::Invocation invocation) → dynamic
+ method noSuchMethod(final core::Invocation invocation) → dynamic
return super.{core::Object::noSuchMethod}(invocation);
}
-static method acceptsInt(core::int x) → void {}
-static method testConflictWithNoSuchMethodForwarderIfImplementedInMixin(self::C c) → void {
+static method acceptsInt(final core::int x) → void {}
+static method testConflictWithNoSuchMethodForwarderIfImplementedInMixin(final self::C c) → void {
if(!(c.{self::C::_f2}{core::int?} == null)) {
core::int? x = c.{self::C::_f2}{core::int?};
x = null;
}
}
-static method testNoConflictWithNoSuchMethodForwarderIfImplementedInMixin1(self::C c) → void {
+static method testNoConflictWithNoSuchMethodForwarderIfImplementedInMixin1(final self::C c) → void {
if(!(c.{self::C::_f3}{core::int?} == null)) {
- core::int x = let core::int? #t3 = c.{self::C::_f3}{core::int?} in #t3 == null ?{core::int} #t3 as{Unchecked} core::int : #t3{core::int};
+ final core::int x = let core::int? #t3 = c.{self::C::_f3}{core::int?} in #t3 == null ?{core::int} #t3 as{Unchecked} core::int : #t3{core::int};
self::acceptsInt(x);
}
}
-static method testNoConflictWithNoSuchMethodForwarderIfImplementedInMixin2(self::C c) → void {
+static method testNoConflictWithNoSuchMethodForwarderIfImplementedInMixin2(final self::C c) → void {
if(!(c.{self::C::_f4}{core::int?} == null)) {
- core::int x = let core::int? #t4 = c.{self::C::_f4}{core::int?} in #t4 == null ?{core::int} #t4 as{Unchecked} core::int : #t4{core::int};
+ final core::int x = let core::int? #t4 = c.{self::C::_f4}{core::int?} in #t4 == null ?{core::int} #t4 as{Unchecked} core::int : #t4{core::int};
self::acceptsInt(x);
}
}
static method main() → dynamic {
{
- synthesized core::Iterator<self::C> #forIterator = <self::C>[new self::C::•(null), new self::C::•(0)].{core::Iterable::iterator}{core::Iterator<self::C>};
+ final synthesized core::Iterator<self::C> #forIterator = <self::C>[new self::C::•(null), new self::C::•(0)].{core::Iterable::iterator}{core::Iterator<self::C>};
for (; #forIterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
- self::C c = #forIterator.{core::Iterator::current}{self::C};
+ final self::C c = #forIterator.{core::Iterator::current}{self::C};
{
self::testConflictWithNoSuchMethodForwarderIfImplementedInMixin(c);
self::testNoConflictWithNoSuchMethodForwarderIfImplementedInMixin1(c);
diff --git a/pkg/front_end/testcases/dart2wasm/issue53239.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2wasm/issue53239.dart.strong.transformed.expect
index 2fb7e13..3ea53f0 100644
--- a/pkg/front_end/testcases/dart2wasm/issue53239.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2wasm/issue53239.dart.strong.transformed.expect
@@ -4,11 +4,11 @@
class C1 extends core::Object {
final field core::int id;
- constructor n1(core::int id, [core::String s = #C1]) → self::C1
+ constructor n1(final core::int id, [final core::String s = #C1]) → self::C1
: self::C1::id = id, super core::Object::•() {
self::log = s;
}
- static method _#n1#tearOff(core::int id, [core::String s = #C1]) → self::C1
+ static method _#n1#tearOff(final core::int id, [final core::String s = #C1]) → self::C1
return new self::C1::n1(id, s);
}
extension type ET1(core::int id) {
@@ -19,30 +19,30 @@
constructor tearoff n1 = self::ET1|constructor#_#n1#tearOff;
}
static field dynamic log;
-static extension-type-member method ET1|constructor#(core::int id) → self::ET1% /* erasure=core::int, declared=! */ {
+static extension-type-member method ET1|constructor#(final core::int id) → self::ET1% /* erasure=core::int, declared=! */ {
lowered final self::ET1% /* erasure=core::int, declared=! */ #this = id;
return #this;
}
-static extension-type-member method ET1|constructor#_#new#tearOff(core::int id) → self::ET1% /* erasure=core::int, declared=! */
+static extension-type-member method ET1|constructor#_#new#tearOff(final core::int id) → self::ET1% /* erasure=core::int, declared=! */
return self::ET1|constructor#(id);
-static extension-type-member method ET1|constructor#n1(core::int id, [core::String s = #C2]) → self::ET1% /* erasure=core::int, declared=! */ {
+static extension-type-member method ET1|constructor#n1(final core::int id, [final core::String s = #C2]) → self::ET1% /* erasure=core::int, declared=! */ {
lowered final self::ET1% /* erasure=core::int, declared=! */ #this = id;
{
self::log = s;
}
return #this;
}
-static extension-type-member method ET1|constructor#_#n1#tearOff(core::int id, [core::String s = #C2]) → self::ET1% /* erasure=core::int, declared=! */
+static extension-type-member method ET1|constructor#_#n1#tearOff(final core::int id, [final core::String s = #C2]) → self::ET1% /* erasure=core::int, declared=! */
return self::ET1|constructor#n1(id, s);
static method main() → dynamic {
- (core::int, [core::String]) → self::C1 x = #C3;
+ final (core::int, [core::String]) → self::C1 x = #C3;
x(0){(core::int, [core::String]) → self::C1};
self::expect("a", self::log);
- (core::int, [core::String]) → self::ET1% /* erasure=core::int, declared=! */ y = #C4;
+ final (core::int, [core::String]) → self::ET1% /* erasure=core::int, declared=! */ y = #C4;
y(1){(core::int, [core::String]) → self::ET1% /* erasure=core::int, declared=! */};
self::expect("b", self::log);
}
-static method expect(dynamic expected, dynamic actual) → dynamic {
+static method expect(final dynamic expected, final dynamic actual) → dynamic {
if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual))
throw "Expected ${expected}, actual ${actual}";
}
diff --git a/pkg/front_end/testcases/dart2wasm/issue54069.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2wasm/issue54069.dart.strong.transformed.expect
index d5d60ca..7190b53 100644
--- a/pkg/front_end/testcases/dart2wasm/issue54069.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2wasm/issue54069.dart.strong.transformed.expect
@@ -8,7 +8,7 @@
static method getBinaryTestProto() → asy::Future<typ::Uint8List>
return self::readFileWeb("test.binary.pb");
-static method readFileWeb(core::String path) → asy::Future<typ::Uint8List> async /* emittedValueType= typ::Uint8List */ {
+static method readFileWeb(final core::String path) → asy::Future<typ::Uint8List> async /* emittedValueType= typ::Uint8List */ {
throw "";
}
static method runBench([typ::Uint8List? data = #C1]) → void async /* emittedValueType= void */ {
@@ -27,7 +27,7 @@
static method main() → void async /* emittedValueType= void */ {
typ::Uint8List :async_temporary_0;
:async_temporary_0 = await self::getBinaryTestProto();
- typ::Uint8List data = :async_temporary_0 as dynamic;
+ final typ::Uint8List data = :async_temporary_0 as dynamic;
core::print("File successfully read, contents: ${data}");
self::runBench(data);
}
diff --git a/pkg/front_end/testcases/dart2wasm/issue55529.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2wasm/issue55529.dart.strong.transformed.expect
index cac766a..3a2b8ee 100644
--- a/pkg/front_end/testcases/dart2wasm/issue55529.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2wasm/issue55529.dart.strong.transformed.expect
@@ -4,36 +4,36 @@
class A1 extends core::Object {
final field core::Object? a;
- constructor •({required core::Object? a}) → self::A1
+ constructor •({required final core::Object? a}) → self::A1
: self::A1::a = a, super core::Object::•()
;
- static method _#new#tearOff({required core::Object? a}) → self::A1
+ static method _#new#tearOff({required final core::Object? a}) → self::A1
return new self::A1::•(a: a);
}
class B1 extends self::A1 {
- constructor •({core::Object? a = #C1}) → self::B1
+ constructor •({final core::Object? a = #C1}) → self::B1
: super self::A1::•(a: a) {}
- static method _#new#tearOff({core::Object? a = #C1}) → self::B1
+ static method _#new#tearOff({final core::Object? a = #C1}) → self::B1
return new self::B1::•(a: a);
}
class A2 extends core::Object {
final field core::Object? a;
- constructor •(core::Object? a) → self::A2
+ constructor •(final core::Object? a) → self::A2
: self::A2::a = a, super core::Object::•()
;
- static method _#new#tearOff(core::Object? a) → self::A2
+ static method _#new#tearOff(final core::Object? a) → self::A2
return new self::A2::•(a);
}
class B2 extends self::A2 {
- constructor •([core::Object? a = #C1]) → self::B2
+ constructor •([final core::Object? a = #C1]) → self::B2
: super self::A2::•(a)
;
- static method _#new#tearOff([core::Object? a = #C1]) → self::B2
+ static method _#new#tearOff([final core::Object? a = #C1]) → self::B2
return new self::B2::•(a);
}
static method main() → void {
- ({a: core::Object?}) → self::B1 f1 = #C2;
- ([core::Object?]) → self::B2 f2 = #C3;
+ final ({a: core::Object?}) → self::B1 f1 = #C2;
+ final ([core::Object?]) → self::B2 f2 = #C3;
}
constants {
diff --git a/pkg/front_end/testcases/dart2wasm/yield.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2wasm/yield.dart.strong.transformed.expect
index 123755a..7ea2824 100644
--- a/pkg/front_end/testcases/dart2wasm/yield.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2wasm/yield.dart.strong.transformed.expect
@@ -3,19 +3,19 @@
import "dart:core" as core;
import "dart:async" as asy;
-static method method(core::Iterable<core::int> iterable) → core::Iterable<core::int> sync* /* emittedValueType= core::int */ {
+static method method(final core::Iterable<core::int> iterable) → core::Iterable<core::int> sync* /* emittedValueType= core::int */ {
yield 1;
yield 2;
yield* iterable;
}
-static method asyncMethod(asy::Stream<core::int> stream) → asy::Stream<core::int> {
- synthesized asy::StreamController<core::int> #controller = asy::StreamController::•<core::int>(sync: #C1);
- synthesized () → asy::Future<void> #body = () → asy::Future<void> async /* emittedValueType= void */ {
+static method asyncMethod(final asy::Stream<core::int> stream) → asy::Stream<core::int> {
+ final synthesized asy::StreamController<core::int> #controller = asy::StreamController::•<core::int>(sync: #C1);
+ final synthesized () → asy::Future<void> #body = () → asy::Future<void> async /* emittedValueType= void */ {
void :async_temporary_0;
void :async_temporary_1;
dynamic :async_temporary_2;
synthesized asy::Completer<void>? #paused;
- dynamic #onCancelCallback = () → void {
+ final dynamic #onCancelCallback = () → void {
if(#paused == null) {
}
else {
@@ -62,7 +62,7 @@
return;
}
}
- on dynamic catch(dynamic #t4, core::StackTrace #t5) {
+ on dynamic catch(final dynamic #t4, final core::StackTrace #t5) {
#controller.{asy::StreamController::addError}(#t4, #t5){(core::Object, [core::StackTrace?]) → void};
#t4;
#t5;
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/int_operations_dart2wasm.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/int_operations_dart2wasm.dart.expect
index 9fab9e8..872e73f 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/int_operations_dart2wasm.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/int_operations_dart2wasm.dart.expect
@@ -12,7 +12,7 @@
static field core::int y = [@vm.inferred-type.metadata=dart.core::_BoxedInt] core::int::parse("3");
[@vm.unboxing-info.metadata=(i,i,i)->b]
-static method use([@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] dynamic a, [@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] dynamic b, [@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] dynamic c) → void {
+static method use([@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] final dynamic a, [@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] final dynamic b, [@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] final dynamic c) → void {
core::print(a);
core::print(b);
core::print(c);
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/records_dart2wasm.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/records_dart2wasm.dart.expect
index 56ebbff..b47330d 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/records_dart2wasm.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/records_dart2wasm.dart.expect
@@ -7,13 +7,13 @@
static field dynamic list = <core::Object>["abc", (42, {foo42: "foo42"})];
[@vm.unboxing-info.metadata=(i,i,i)->b]
-static method recordLiteral([@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] dynamic x, [@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] dynamic y, [@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] dynamic z) → dynamic
+static method recordLiteral([@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] final dynamic x, [@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] final dynamic y, [@vm.inferred-arg-type.metadata=dart.core::_BoxedInt] final dynamic z) → dynamic
return (x, y, {bar: z});
-static method recordFieldAccess1([@vm.inferred-arg-type.metadata=dart.core::Record_2](core::int, core::String) rec) → dynamic
+static method recordFieldAccess1([@vm.inferred-arg-type.metadata=dart.core::Record_2] final(core::int, core::String) rec) → dynamic
return rec.$1{core::int};
-static method recordFieldAccess2([@vm.inferred-arg-type.metadata=dart.core::Record_0_a_b]({required a: core::int, required b: core::String}) rec) → dynamic
+static method recordFieldAccess2([@vm.inferred-arg-type.metadata=dart.core::Record_0_a_b] final({required a: core::int, required b: core::String}) rec) → dynamic
return rec.a{core::int};
-static method recordDynamicFieldAccess(dynamic x) → dynamic
+static method recordDynamicFieldAccess(final dynamic x) → dynamic
return x{dynamic}.foo42;
static method main() → dynamic {
core::print(#C4);