[cfe] Propagate isRedirectingFactoryConstructor flag when patching
Bug: https://github.com/dart-lang/sdk/issues/45101
Change-Id: Id0c9f3ce319111a79b2085f44a4e4243dbe6597a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/187440
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
diff --git a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
index 4c89f92..ae2dc15 100644
--- a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
@@ -198,6 +198,8 @@
origin.procedure.isExternal = _procedure.isExternal;
origin.procedure.function = _procedure.function;
origin.procedure.function.parent = origin.procedure;
+ origin.procedure.isRedirectingFactoryConstructor =
+ _procedure.isRedirectingFactoryConstructor;
return 1;
}
}
@@ -712,6 +714,7 @@
bodyInternal = new RedirectingFactoryBody(target, typeArguments);
function.body = bodyInternal;
bodyInternal?.parent = function;
+ procedure.isRedirectingFactoryConstructor = true;
if (isPatch) {
if (function.typeParameters != null) {
Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
diff --git a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart b/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart
index 0d82df2..cdbabbe 100644
--- a/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/redirecting_factory_body.dart
@@ -6,22 +6,7 @@
library fasta.redirecting_factory_body;
-import 'package:kernel/ast.dart'
- show
- DartType,
- Expression,
- ExpressionStatement,
- Field,
- FunctionNode,
- InvalidExpression,
- Let,
- Member,
- NullLiteral,
- Procedure,
- StaticGet,
- StringLiteral,
- TypeParameterType,
- VariableDeclaration;
+import 'package:kernel/ast.dart';
import 'package:kernel/type_algebra.dart' show Substitution;
@@ -44,6 +29,10 @@
/// information in a factory method body.
const String letName = "#redirecting_factory";
+/// Name used for a synthesized let variable used to encode type arguments to
+/// the redirection target in a factory method body.
+const String varNamePrefix = "#typeArg";
+
class RedirectingFactoryBody extends ExpressionStatement {
RedirectingFactoryBody.internal(Expression value,
[List<DartType> typeArguments])
@@ -104,8 +93,38 @@
..parent = function;
}
+ static bool hasRedirectingFactoryBodyShape(Procedure factory) {
+ if (factory.function.body is! ExpressionStatement) return false;
+ Expression body = (factory.function.body as ExpressionStatement).expression;
+ if (body is Let &&
+ body.variable.name == letName &&
+ body.variable.type is DynamicType &&
+ body.variable.initializer is StaticGet) {
+ Expression currentArgument = body.body;
+ int argumentCount = 0;
+ while (currentArgument is! InvalidExpression) {
+ Expression argument = currentArgument;
+ if (argument is Let) {
+ String argumentName = "${varNamePrefix}${argumentCount}";
+ if (argument.variable.name != argumentName) {
+ return false;
+ }
+ if (argument.variable.initializer is! NullLiteral) {
+ return false;
+ }
+ currentArgument = argument.body;
+ ++argumentCount;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
static Expression encodeTypeArguments(List<DartType> typeArguments) {
- String varNamePrefix = "#typeArg";
Expression result = new InvalidExpression(null);
if (typeArguments == null) {
return result;
diff --git a/pkg/front_end/lib/src/fasta/kernel/verifier.dart b/pkg/front_end/lib/src/fasta/kernel/verifier.dart
index 468697e..9a23516 100644
--- a/pkg/front_end/lib/src/fasta/kernel/verifier.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/verifier.dart
@@ -29,7 +29,10 @@
import '../type_inference/type_schema.dart' show UnknownType;
import 'redirecting_factory_body.dart'
- show RedirectingFactoryBody, getRedirectingFactoryBody;
+ show
+ RedirectingFactoryBody,
+ getRedirectingFactoryBody,
+ isRedirectingFactory;
List<LocatedMessage> verifyComponent(Component component,
{bool isOutline, bool afterConst, bool skipPlatform: false}) {
@@ -220,7 +223,11 @@
@override
void visitLibrary(Library node) {
// Issue(http://dartbug.com/32530)
- if (skipPlatform && node.importUri.scheme == 'dart') {
+ // 'dart:test' is used in the unit tests and isn't an actual part of the
+ // platform.
+ if (skipPlatform &&
+ node.importUri.scheme == 'dart' &&
+ node.importUri.path != 'test') {
return;
}
@@ -260,6 +267,22 @@
void visitProcedure(Procedure node) {
enterTreeNode(node);
fileUri = checkLocation(node, node.name.text, node.fileUri);
+
+ // TODO(dmitryas): Investigate why some redirecting factory bodies retain
+ // the shape, but aren't of the RedirectingFactoryBody type.
+ bool hasBody = isRedirectingFactory(node) ||
+ RedirectingFactoryBody.hasRedirectingFactoryBodyShape(node);
+ bool hasFlag = node.isRedirectingFactoryConstructor;
+ if (hasBody != hasFlag) {
+ String hasBodyString = hasBody ? "has" : "doesn't have";
+ String hasFlagString = hasFlag ? "has" : "doesn't have";
+ problem(
+ node,
+ "Procedure '${node.name}' ${hasBodyString} a body "
+ "of a redirecting factory, but ${hasFlagString} the "
+ "'isRedirectingFactoryConstructor' bit set.");
+ }
+
super.visitProcedure(node);
exitTreeNode(node);
}
@@ -422,7 +445,11 @@
@override
visitLibrary(Library node) {
- if (skipPlatform && node.importUri.scheme == 'dart') {
+ // 'dart:test' is used in the unit tests and isn't an actual part of the
+ // platform.
+ if (skipPlatform &&
+ node.importUri.scheme == 'dart' &&
+ node.importUri.path != "test") {
return;
}
diff --git a/pkg/front_end/test/desugar_test.dart b/pkg/front_end/test/desugar_test.dart
index 3ebad1e..7aa716d 100644
--- a/pkg/front_end/test/desugar_test.dart
+++ b/pkg/front_end/test/desugar_test.dart
@@ -54,13 +54,11 @@
var component = new ir.Component();
new BinaryBuilder(new File.fromUri(componentUri).readAsBytesSync())
.readComponent(component);
- checkIsRedirectingFactory(component, 'collection', 'HashMap', 'identity',
- isPatch: true);
+ checkIsRedirectingFactory(component, 'collection', 'HashMap', 'identity');
}
void checkIsRedirectingFactory(ir.Component component, String uriPath,
- String className, String constructorName,
- {bool isPatch: false}) {
+ String className, String constructorName) {
var lib =
component.libraries.firstWhere((l) => l.importUri.path.endsWith(uriPath));
var cls = lib.classes.firstWhere((c) => c.name == className);
@@ -69,8 +67,7 @@
Expect.isTrue(
member.kind == ir.ProcedureKind.Factory, "$member is not a factory");
Expect.isTrue(api.isRedirectingFactory(member));
- // TODO: this should always be true. Issue #33495
- Expect.equals(!isPatch, member.isRedirectingFactoryConstructor);
+ Expect.isTrue(member.isRedirectingFactoryConstructor);
}
const aSource = '''
diff --git a/pkg/front_end/testcases/general/issue45101/libraries.json b/pkg/front_end/testcases/general/issue45101/libraries.json
new file mode 100644
index 0000000..154c73c
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45101/libraries.json
@@ -0,0 +1,12 @@
+{
+ "none": {
+ "libraries": {
+ "test": {
+ "patches": [
+ "patch_lib.dart"
+ ],
+ "uri": "origin_lib.dart"
+ }
+ }
+ }
+}
diff --git a/pkg/front_end/testcases/general/issue45101/main.dart b/pkg/front_end/testcases/general/issue45101/main.dart
new file mode 100644
index 0000000..171b59b
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45101/main.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, 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.
+// @dart=2.9
+import 'dart:test';
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45101/main.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue45101/main.dart.textual_outline.expect
new file mode 100644
index 0000000..9ededd9
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45101/main.dart.textual_outline.expect
@@ -0,0 +1,4 @@
+// @dart = 2.9
+import 'dart:test';
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45101/main.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue45101/main.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..9ededd9
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45101/main.dart.textual_outline_modelled.expect
@@ -0,0 +1,4 @@
+// @dart = 2.9
+import 'dart:test';
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue45101/main.dart.weak.expect b/pkg/front_end/testcases/general/issue45101/main.dart.weak.expect
new file mode 100644
index 0000000..8c7b289
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45101/main.dart.weak.expect
@@ -0,0 +1,60 @@
+library;
+import self as self;
+
+import "dart:test";
+
+static method main() → dynamic {}
+
+library;
+import self as self2;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+import "dart:_internal";
+
+class _ArraySize<T extends core::Object* = dynamic> extends core::Object implements self2::Array<self2::_ArraySize::T*> /*hasConstConstructor*/ { // from org-dartlang-testcase:///patch_lib.dart
+ final field core::int* foo;
+ const constructor •(core::int* foo) → self2::_ArraySize<self2::_ArraySize::T*>*
+ : self2::_ArraySize::foo = foo, super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+ abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+ abstract member-signature method toString() → core::String*; -> core::Object::toString
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+@#C1
+@#C4
+class Array<T extends core::Object* = dynamic> extends core::Object {
+ static final field dynamic _redirecting# = <dynamic>[self2::Array::•];
+ @#C1
+ static factory /* from org-dartlang-testcase:///patch_lib.dart */ •<T extends core::Object* = dynamic>(core::int* foo) → self2::Array<self2::Array::•::T*>*
+ let dynamic #redirecting_factory = self2::_ArraySize::• in let self2::Array::•::T* #typeArg0 = null in invalid-expression;
+ abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+ abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+ abstract member-signature method toString() → core::String*; -> core::Object::toString
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+
+constants {
+ #C1 = _in::_Patch {}
+ #C2 = "vm:entry-point"
+ #C3 = null
+ #C4 = core::pragma {name:#C2, options:#C3}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///origin_lib.dart:
+- pragma._ (from org-dartlang-sdk:///sdk/lib/core/annotations.dart:188:9)
diff --git a/pkg/front_end/testcases/general/issue45101/main.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue45101/main.dart.weak.outline.expect
new file mode 100644
index 0000000..c56c375
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45101/main.dart.weak.outline.expect
@@ -0,0 +1,56 @@
+library;
+import self as self;
+
+import "dart:test";
+
+static method main() → dynamic
+ ;
+
+library;
+import self as self2;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+import "dart:_internal";
+
+class _ArraySize<T extends core::Object* = dynamic> extends core::Object implements self2::Array<self2::_ArraySize::T*> /*hasConstConstructor*/ { // from org-dartlang-testcase:///patch_lib.dart
+ final field core::int* foo;
+ const constructor •(core::int* foo) → self2::_ArraySize<self2::_ArraySize::T*>*
+ : self2::_ArraySize::foo = foo, super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+ abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+ abstract member-signature method toString() → core::String*; -> core::Object::toString
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+@_in::patch
+@core::pragma::_("vm:entry-point")
+class Array<T extends core::Object* = dynamic> extends core::Object {
+ static final field dynamic _redirecting# = <dynamic>[self2::Array::•];
+ @_in::patch
+ external static factory •<T extends core::Object* = dynamic>(core::int* foo) → self2::Array<self2::Array::•::T*>*
+ let dynamic #redirecting_factory = self2::_ArraySize::• in let self2::Array::•::T* #typeArg0 = null in invalid-expression;
+ abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+ abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+ abstract member-signature method toString() → core::String*; -> core::Object::toString
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+
+
+Extra constant evaluation status:
+Evaluated: StaticGet @ org-dartlang-testcase:///origin_lib.dart:10:1 -> InstanceConstant(const _Patch{})
+Evaluated: ConstructorInvocation @ (unknown position in org-dartlang-testcase:///origin_lib.dart) -> InstanceConstant(const pragma{pragma.name: "vm:entry-point", pragma.options: null})
+Evaluated: StaticGet @ (unknown position in org-dartlang-testcase:///origin_lib.dart) -> InstanceConstant(const _Patch{})
+Extra constant evaluation: evaluated: 9, effectively constant: 3
diff --git a/pkg/front_end/testcases/general/issue45101/main.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue45101/main.dart.weak.transformed.expect
new file mode 100644
index 0000000..8c7b289
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45101/main.dart.weak.transformed.expect
@@ -0,0 +1,60 @@
+library;
+import self as self;
+
+import "dart:test";
+
+static method main() → dynamic {}
+
+library;
+import self as self2;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+import "dart:_internal";
+
+class _ArraySize<T extends core::Object* = dynamic> extends core::Object implements self2::Array<self2::_ArraySize::T*> /*hasConstConstructor*/ { // from org-dartlang-testcase:///patch_lib.dart
+ final field core::int* foo;
+ const constructor •(core::int* foo) → self2::_ArraySize<self2::_ArraySize::T*>*
+ : self2::_ArraySize::foo = foo, super core::Object::•()
+ ;
+ abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+ abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+ abstract member-signature method toString() → core::String*; -> core::Object::toString
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+@#C1
+@#C4
+class Array<T extends core::Object* = dynamic> extends core::Object {
+ static final field dynamic _redirecting# = <dynamic>[self2::Array::•];
+ @#C1
+ static factory /* from org-dartlang-testcase:///patch_lib.dart */ •<T extends core::Object* = dynamic>(core::int* foo) → self2::Array<self2::Array::•::T*>*
+ let dynamic #redirecting_factory = self2::_ArraySize::• in let self2::Array::•::T* #typeArg0 = null in invalid-expression;
+ abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
+ abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
+ abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
+ abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
+ abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
+ abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
+ abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
+ abstract member-signature method toString() → core::String*; -> core::Object::toString
+ abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
+ abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
+}
+
+constants {
+ #C1 = _in::_Patch {}
+ #C2 = "vm:entry-point"
+ #C3 = null
+ #C4 = core::pragma {name:#C2, options:#C3}
+}
+
+
+Constructor coverage from constants:
+org-dartlang-testcase:///origin_lib.dart:
+- pragma._ (from org-dartlang-sdk:///sdk/lib/core/annotations.dart:188:9)
diff --git a/pkg/front_end/testcases/general/issue45101/origin_lib.dart b/pkg/front_end/testcases/general/issue45101/origin_lib.dart
new file mode 100644
index 0000000..2aabe5a
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45101/origin_lib.dart
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, 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.
+// @dart=2.9
+
+class Array<T> {
+ external const factory Array(int foo);
+}
diff --git a/pkg/front_end/testcases/general/issue45101/patch_lib.dart b/pkg/front_end/testcases/general/issue45101/patch_lib.dart
new file mode 100644
index 0000000..b5a1fd5
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue45101/patch_lib.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, 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.
+// @dart=2.9
+// ignore: import_internal_library
+import 'dart:_internal';
+
+@patch
+@pragma("vm:entry-point")
+class Array<T> {
+ // ...
+
+ @patch
+ const factory Array(int foo) = _ArraySize<T>;
+}
+
+class _ArraySize<T> implements Array<T> {
+ final int foo;
+
+ const _ArraySize(this.foo);
+}