mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
[cfe] Handle un-inlined fields/locals in eager instantiation constantification
Closes #47108 TEST=existing Change-Id: I590939161cde1316eede7d828dff5fe6ac2790fd Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212577 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
This commit is contained in:
parent
015c4e604a
commit
bc1c71fd9b
|
@ -233,7 +233,7 @@ class Dart2jsTarget extends Target {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConstantsBackend constantsBackend(CoreTypes coreTypes) =>
|
ConstantsBackend get constantsBackend =>
|
||||||
const Dart2jsConstantsBackend(supportsUnevaluatedConstants: true);
|
const Dart2jsConstantsBackend(supportsUnevaluatedConstants: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,8 +98,12 @@ class DevCompilerConstantsBackend extends ConstantsBackend {
|
||||||
@override
|
@override
|
||||||
NumberSemantics get numberSemantics => NumberSemantics.js;
|
NumberSemantics get numberSemantics => NumberSemantics.js;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get alwaysInlineConstants => false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldInlineConstant(ConstantExpression initializer) {
|
bool shouldInlineConstant(ConstantExpression initializer) {
|
||||||
|
assert(!alwaysInlineConstants);
|
||||||
var constant = initializer.constant;
|
var constant = initializer.constant;
|
||||||
if (constant is StringConstant) {
|
if (constant is StringConstant) {
|
||||||
// Only inline small string constants, not large ones.
|
// Only inline small string constants, not large ones.
|
||||||
|
|
|
@ -243,8 +243,7 @@ class DevCompilerTarget extends Target {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConstantsBackend constantsBackend(CoreTypes coreTypes) =>
|
ConstantsBackend get constantsBackend => const DevCompilerConstantsBackend();
|
||||||
const DevCompilerConstantsBackend();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Analyzes a component to determine if any covariance checks in private
|
/// Analyzes a component to determine if any covariance checks in private
|
||||||
|
|
|
@ -848,6 +848,8 @@ class ProcessedOptions {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompilerOptions get rawOptionsForTesting => _raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [FileSystem] that only allows access to files that have been explicitly
|
/// A [FileSystem] that only allows access to files that have been explicitly
|
||||||
|
|
|
@ -691,6 +691,14 @@ class ConstantsTransformer extends RemovingTransformer {
|
||||||
Instantiation result =
|
Instantiation result =
|
||||||
super.visitInstantiation(node, removalSentinel) as Instantiation;
|
super.visitInstantiation(node, removalSentinel) as Instantiation;
|
||||||
Expression expression = result.expression;
|
Expression expression = result.expression;
|
||||||
|
if (expression is StaticGet && expression.target.isConst) {
|
||||||
|
// Handle [StaticGet] of constant fields also when these are not inlined.
|
||||||
|
expression = (expression.target as Field).initializer!;
|
||||||
|
} else if (expression is VariableGet && expression.variable.isConst) {
|
||||||
|
// Handle [VariableGet] of constant locals also when these are not
|
||||||
|
// inlined.
|
||||||
|
expression = expression.variable.initializer!;
|
||||||
|
}
|
||||||
if (expression is ConstantExpression) {
|
if (expression is ConstantExpression) {
|
||||||
if (result.typeArguments.every(isInstantiated)) {
|
if (result.typeArguments.every(isInstantiated)) {
|
||||||
return evaluateAndTransformWithContext(node, result);
|
return evaluateAndTransformWithContext(node, result);
|
||||||
|
@ -882,6 +890,9 @@ class ConstantsTransformer extends RemovingTransformer {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldInline(Expression initializer) {
|
bool shouldInline(Expression initializer) {
|
||||||
|
if (backend.alwaysInlineConstants) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (initializer is ConstantExpression) {
|
if (initializer is ConstantExpression) {
|
||||||
return backend.shouldInlineConstant(initializer);
|
return backend.shouldInlineConstant(initializer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1234,7 +1234,7 @@ class KernelTarget extends TargetImplementation {
|
||||||
constants.ConstantEvaluationData constantEvaluationData =
|
constants.ConstantEvaluationData constantEvaluationData =
|
||||||
constants.transformLibraries(
|
constants.transformLibraries(
|
||||||
loader.libraries,
|
loader.libraries,
|
||||||
backendTarget.constantsBackend(loader.coreTypes),
|
backendTarget.constantsBackend,
|
||||||
environmentDefines,
|
environmentDefines,
|
||||||
environment,
|
environment,
|
||||||
new KernelConstantErrorReporter(loader),
|
new KernelConstantErrorReporter(loader),
|
||||||
|
@ -1290,7 +1290,7 @@ class KernelTarget extends TargetImplementation {
|
||||||
|
|
||||||
constants.transformProcedure(
|
constants.transformProcedure(
|
||||||
procedure,
|
procedure,
|
||||||
backendTarget.constantsBackend(loader.coreTypes),
|
backendTarget.constantsBackend,
|
||||||
environmentDefines,
|
environmentDefines,
|
||||||
environment,
|
environment,
|
||||||
new KernelConstantErrorReporter(loader),
|
new KernelConstantErrorReporter(loader),
|
||||||
|
|
|
@ -33,8 +33,8 @@ import 'redirecting_factory_body.dart'
|
||||||
|
|
||||||
List<LocatedMessage> verifyComponent(Component component, Target target,
|
List<LocatedMessage> verifyComponent(Component component, Target target,
|
||||||
{bool? isOutline, bool? afterConst, bool skipPlatform: false}) {
|
{bool? isOutline, bool? afterConst, bool skipPlatform: false}) {
|
||||||
FastaVerifyingVisitor verifier =
|
FastaVerifyingVisitor verifier = new FastaVerifyingVisitor(target,
|
||||||
new FastaVerifyingVisitor(target, isOutline, afterConst, skipPlatform);
|
isOutline: isOutline, afterConst: afterConst, skipPlatform: skipPlatform);
|
||||||
component.accept(verifier);
|
component.accept(verifier);
|
||||||
return verifier.errors;
|
return verifier.errors;
|
||||||
}
|
}
|
||||||
|
@ -47,9 +47,13 @@ class FastaVerifyingVisitor extends VerifyingVisitor {
|
||||||
final List<TreeNode> treeNodeStack = <TreeNode>[];
|
final List<TreeNode> treeNodeStack = <TreeNode>[];
|
||||||
final bool skipPlatform;
|
final bool skipPlatform;
|
||||||
|
|
||||||
FastaVerifyingVisitor(
|
FastaVerifyingVisitor(this.target,
|
||||||
this.target, bool? isOutline, bool? afterConst, this.skipPlatform)
|
{bool? isOutline, bool? afterConst, required this.skipPlatform})
|
||||||
: super(isOutline: isOutline, afterConst: afterConst);
|
: super(
|
||||||
|
isOutline: isOutline,
|
||||||
|
afterConst: afterConst,
|
||||||
|
constantsAreAlwaysInlined:
|
||||||
|
target.constantsBackend.alwaysInlineConstants);
|
||||||
|
|
||||||
/// Invoked by all visit methods if the visited node is a [TreeNode].
|
/// Invoked by all visit methods if the visited node is a [TreeNode].
|
||||||
void enterTreeNode(TreeNode node) {
|
void enterTreeNode(TreeNode node) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ void benchmark(Component component, List<Library> libraries) {
|
||||||
stopwatch.reset();
|
stopwatch.reset();
|
||||||
CoreTypes coreTypes = new CoreTypes(component);
|
CoreTypes coreTypes = new CoreTypes(component);
|
||||||
ConstantsBackend constantsBackend =
|
ConstantsBackend constantsBackend =
|
||||||
target.backendTarget.constantsBackend(coreTypes);
|
target.backendTarget.constantsBackend;
|
||||||
ClassHierarchy hierarchy = new ClassHierarchy(component, coreTypes);
|
ClassHierarchy hierarchy = new ClassHierarchy(component, coreTypes);
|
||||||
TypeEnvironment environment = new TypeEnvironment(coreTypes, hierarchy);
|
TypeEnvironment environment = new TypeEnvironment(coreTypes, hierarchy);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
|
|
@ -879,8 +879,7 @@ class StressConstantEvaluatorStep
|
||||||
Future<Result<ComponentResult>> run(
|
Future<Result<ComponentResult>> run(
|
||||||
ComponentResult result, FastaContext context) async {
|
ComponentResult result, FastaContext context) async {
|
||||||
KernelTarget target = result.sourceTarget;
|
KernelTarget target = result.sourceTarget;
|
||||||
ConstantsBackend constantsBackend =
|
ConstantsBackend constantsBackend = target.backendTarget.constantsBackend;
|
||||||
target.backendTarget.constantsBackend(target.loader.coreTypes);
|
|
||||||
TypeEnvironment environment =
|
TypeEnvironment environment =
|
||||||
new TypeEnvironment(target.loader.coreTypes, target.loader.hierarchy);
|
new TypeEnvironment(target.loader.coreTypes, target.loader.hierarchy);
|
||||||
StressConstantEvaluatorVisitor stressConstantEvaluatorVisitor =
|
StressConstantEvaluatorVisitor stressConstantEvaluatorVisitor =
|
||||||
|
@ -1971,19 +1970,20 @@ class Verify extends Step<ComponentResult, ComponentResult, FastaContext> {
|
||||||
|
|
||||||
Component component = result.component;
|
Component component = result.component;
|
||||||
StringBuffer messages = new StringBuffer();
|
StringBuffer messages = new StringBuffer();
|
||||||
ProcessedOptions options = new ProcessedOptions(
|
void Function(DiagnosticMessage)? previousOnDiagnostics =
|
||||||
options: new CompilerOptions()
|
result.options.rawOptionsForTesting.onDiagnostic;
|
||||||
..onDiagnostic = (DiagnosticMessage message) {
|
result.options.rawOptionsForTesting.onDiagnostic =
|
||||||
if (messages.isNotEmpty) {
|
(DiagnosticMessage message) {
|
||||||
messages.write("\n");
|
if (messages.isNotEmpty) {
|
||||||
}
|
messages.write("\n");
|
||||||
messages.writeAll(message.plainTextFormatted, "\n");
|
}
|
||||||
});
|
messages.writeAll(message.plainTextFormatted, "\n");
|
||||||
return await CompilerContext.runWithOptions(options,
|
};
|
||||||
(compilerContext) async {
|
Result<ComponentResult> verifyResult = await CompilerContext.runWithOptions(
|
||||||
|
result.options, (compilerContext) async {
|
||||||
compilerContext.uriToSource.addAll(component.uriToSource);
|
compilerContext.uriToSource.addAll(component.uriToSource);
|
||||||
List<LocatedMessage> verificationErrors = verifyComponent(
|
List<LocatedMessage> verificationErrors = verifyComponent(
|
||||||
component, options.target,
|
component, result.options.target,
|
||||||
isOutline: !fullCompile, skipPlatform: true);
|
isOutline: !fullCompile, skipPlatform: true);
|
||||||
assert(verificationErrors.isEmpty || messages.isNotEmpty);
|
assert(verificationErrors.isEmpty || messages.isNotEmpty);
|
||||||
if (messages.isEmpty) {
|
if (messages.isEmpty) {
|
||||||
|
@ -1993,6 +1993,8 @@ class Verify extends Step<ComponentResult, ComponentResult, FastaContext> {
|
||||||
null, context.expectationSet["VerificationError"], "$messages");
|
null, context.expectationSet["VerificationError"], "$messages");
|
||||||
}
|
}
|
||||||
}, errorOnMissingInput: false);
|
}, errorOnMissingInput: false);
|
||||||
|
result.options.rawOptionsForTesting.onDiagnostic = previousOnDiagnostics;
|
||||||
|
return verifyResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import 'package:front_end/src/fasta/kernel/utils.dart' show serializeComponent;
|
||||||
import 'package:front_end/src/fasta/kernel/verifier.dart' show verifyComponent;
|
import 'package:front_end/src/fasta/kernel/verifier.dart' show verifyComponent;
|
||||||
|
|
||||||
import 'package:kernel/ast.dart' show Component;
|
import 'package:kernel/ast.dart' show Component;
|
||||||
|
import 'package:kernel/target/targets.dart';
|
||||||
|
|
||||||
const Map<String, String> files = const <String, String>{
|
const Map<String, String> files = const <String, String>{
|
||||||
"repro.dart": """
|
"repro.dart": """
|
||||||
|
@ -88,7 +89,8 @@ Future<void> test() async {
|
||||||
options = new CompilerOptions()
|
options = new CompilerOptions()
|
||||||
..fileSystem = fs
|
..fileSystem = fs
|
||||||
..additionalDills = <Uri>[base.resolve("lib.dart.dill")]
|
..additionalDills = <Uri>[base.resolve("lib.dart.dill")]
|
||||||
..sdkSummary = platformDill;
|
..sdkSummary = platformDill
|
||||||
|
..target = new NoneTarget(new TargetFlags());
|
||||||
|
|
||||||
List<Uri> inputs = <Uri>[base.resolve("repro.dart")];
|
List<Uri> inputs = <Uri>[base.resolve("repro.dart")];
|
||||||
|
|
||||||
|
|
17
pkg/front_end/testcases/dartdevc/issue47108.dart
Normal file
17
pkg/front_end/testcases/dartdevc/issue47108.dart
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
class C<T> {}
|
||||||
|
|
||||||
|
const constructorTearOff = C.new;
|
||||||
|
|
||||||
|
main() {
|
||||||
|
// These instantiations are in a const context so they appear in the const pool.
|
||||||
|
const instantiatedTearOff = constructorTearOff<int>;
|
||||||
|
const instantiatedTearOff2 = constructorTearOff<int>;
|
||||||
|
print(identical(instantiatedTearOff, instantiatedTearOff2)); // Prints true
|
||||||
|
|
||||||
|
// These instantiations are not in a const context so they don't appear in the const pool.
|
||||||
|
print(identical(constructorTearOff<String>, constructorTearOff<String>)); // Prints false
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
library /*isNonNullableByDefault*/;
|
||||||
|
import self as self;
|
||||||
|
import "dart:core" as core;
|
||||||
|
|
||||||
|
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||||
|
synthetic constructor •() → self::C<self::C::T%>
|
||||||
|
: super core::Object::•()
|
||||||
|
;
|
||||||
|
static method _#new#tearOff<T extends core::Object? = dynamic>() → self::C<self::C::_#new#tearOff::T%>
|
||||||
|
return new self::C::•<self::C::_#new#tearOff::T%>();
|
||||||
|
}
|
||||||
|
static const field <T extends core::Object? = dynamic>() → self::C<T%> constructorTearOff = #C1;
|
||||||
|
static method main() → dynamic {
|
||||||
|
const () → self::C<core::int> instantiatedTearOff = #C2;
|
||||||
|
const () → self::C<core::int> instantiatedTearOff2 = #C2;
|
||||||
|
core::print(core::identical(instantiatedTearOff, instantiatedTearOff2));
|
||||||
|
core::print(core::identical(#C3, #C3));
|
||||||
|
}
|
||||||
|
|
||||||
|
constants {
|
||||||
|
#C1 = static-tearoff self::C::_#new#tearOff
|
||||||
|
#C2 = instantiation self::C::_#new#tearOff <core::int>
|
||||||
|
#C3 = instantiation self::C::_#new#tearOff <core::String>
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
library /*isNonNullableByDefault*/;
|
||||||
|
import self as self;
|
||||||
|
import "dart:core" as core;
|
||||||
|
|
||||||
|
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||||
|
synthetic constructor •() → self::C<self::C::T%>
|
||||||
|
: super core::Object::•()
|
||||||
|
;
|
||||||
|
static method _#new#tearOff<T extends core::Object? = dynamic>() → self::C<self::C::_#new#tearOff::T%>
|
||||||
|
return new self::C::•<self::C::_#new#tearOff::T%>();
|
||||||
|
}
|
||||||
|
static const field <T extends core::Object? = dynamic>() → self::C<T%> constructorTearOff = #C1;
|
||||||
|
static method main() → dynamic {
|
||||||
|
const () → self::C<core::int> instantiatedTearOff = #C2;
|
||||||
|
const () → self::C<core::int> instantiatedTearOff2 = #C2;
|
||||||
|
core::print(core::identical(instantiatedTearOff, instantiatedTearOff2));
|
||||||
|
core::print(core::identical(#C3, #C3));
|
||||||
|
}
|
||||||
|
|
||||||
|
constants {
|
||||||
|
#C1 = static-tearoff self::C::_#new#tearOff
|
||||||
|
#C2 = instantiation self::C::_#new#tearOff <core::int>
|
||||||
|
#C3 = instantiation self::C::_#new#tearOff <core::String>
|
||||||
|
}
|
||||||
|
|
||||||
|
Extra constant evaluation status:
|
||||||
|
Evaluated: StaticInvocation @ org-dartlang-testcase:///issue47108.dart:13:9 -> BoolConstant(true)
|
||||||
|
Evaluated: StaticInvocation @ org-dartlang-testcase:///issue47108.dart:16:9 -> BoolConstant(true)
|
||||||
|
Extra constant evaluation: evaluated: 5, effectively constant: 2
|
|
@ -0,0 +1,4 @@
|
||||||
|
class C<T> {}
|
||||||
|
|
||||||
|
const constructorTearOff = C.new;
|
||||||
|
main() {}
|
|
@ -0,0 +1,4 @@
|
||||||
|
class C<T> {}
|
||||||
|
|
||||||
|
const constructorTearOff = C.new;
|
||||||
|
main() {}
|
24
pkg/front_end/testcases/dartdevc/issue47108.dart.weak.expect
Normal file
24
pkg/front_end/testcases/dartdevc/issue47108.dart.weak.expect
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
library /*isNonNullableByDefault*/;
|
||||||
|
import self as self;
|
||||||
|
import "dart:core" as core;
|
||||||
|
|
||||||
|
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||||
|
synthetic constructor •() → self::C<self::C::T%>
|
||||||
|
: super core::Object::•()
|
||||||
|
;
|
||||||
|
static method _#new#tearOff<T extends core::Object? = dynamic>() → self::C<self::C::_#new#tearOff::T%>
|
||||||
|
return new self::C::•<self::C::_#new#tearOff::T%>();
|
||||||
|
}
|
||||||
|
static const field <T extends core::Object? = dynamic>() → self::C<T%> constructorTearOff = #C1;
|
||||||
|
static method main() → dynamic {
|
||||||
|
const () → self::C<core::int> instantiatedTearOff = #C2;
|
||||||
|
const () → self::C<core::int> instantiatedTearOff2 = #C2;
|
||||||
|
core::print(core::identical(instantiatedTearOff, instantiatedTearOff2));
|
||||||
|
core::print(core::identical(#C3, #C3));
|
||||||
|
}
|
||||||
|
|
||||||
|
constants {
|
||||||
|
#C1 = static-tearoff self::C::_#new#tearOff
|
||||||
|
#C2 = instantiation self::C::_#new#tearOff <core::int*>
|
||||||
|
#C3 = instantiation self::C::_#new#tearOff <core::String*>
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
library /*isNonNullableByDefault*/;
|
||||||
|
import self as self;
|
||||||
|
import "dart:core" as core;
|
||||||
|
|
||||||
|
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||||
|
synthetic constructor •() → self::C<self::C::T%>
|
||||||
|
;
|
||||||
|
static method _#new#tearOff<T extends core::Object? = dynamic>() → self::C<self::C::_#new#tearOff::T%>
|
||||||
|
return new self::C::•<self::C::_#new#tearOff::T%>();
|
||||||
|
}
|
||||||
|
static const field <T extends core::Object? = dynamic>() → self::C<T%> constructorTearOff = self::C::_#new#tearOff;
|
||||||
|
static method main() → dynamic
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
Extra constant evaluation status:
|
||||||
|
Evaluated: StaticTearOff @ org-dartlang-testcase:///issue47108.dart:7:28 -> StaticTearOffConstant(C._#new#tearOff)
|
||||||
|
Extra constant evaluation: evaluated: 2, effectively constant: 1
|
|
@ -0,0 +1,29 @@
|
||||||
|
library /*isNonNullableByDefault*/;
|
||||||
|
import self as self;
|
||||||
|
import "dart:core" as core;
|
||||||
|
|
||||||
|
class C<T extends core::Object? = dynamic> extends core::Object {
|
||||||
|
synthetic constructor •() → self::C<self::C::T%>
|
||||||
|
: super core::Object::•()
|
||||||
|
;
|
||||||
|
static method _#new#tearOff<T extends core::Object? = dynamic>() → self::C<self::C::_#new#tearOff::T%>
|
||||||
|
return new self::C::•<self::C::_#new#tearOff::T%>();
|
||||||
|
}
|
||||||
|
static const field <T extends core::Object? = dynamic>() → self::C<T%> constructorTearOff = #C1;
|
||||||
|
static method main() → dynamic {
|
||||||
|
const () → self::C<core::int> instantiatedTearOff = #C2;
|
||||||
|
const () → self::C<core::int> instantiatedTearOff2 = #C2;
|
||||||
|
core::print(core::identical(instantiatedTearOff, instantiatedTearOff2));
|
||||||
|
core::print(core::identical(#C3, #C3));
|
||||||
|
}
|
||||||
|
|
||||||
|
constants {
|
||||||
|
#C1 = static-tearoff self::C::_#new#tearOff
|
||||||
|
#C2 = instantiation self::C::_#new#tearOff <core::int*>
|
||||||
|
#C3 = instantiation self::C::_#new#tearOff <core::String*>
|
||||||
|
}
|
||||||
|
|
||||||
|
Extra constant evaluation status:
|
||||||
|
Evaluated: StaticInvocation @ org-dartlang-testcase:///issue47108.dart:13:9 -> BoolConstant(true)
|
||||||
|
Evaluated: StaticInvocation @ org-dartlang-testcase:///issue47108.dart:16:9 -> BoolConstant(true)
|
||||||
|
Extra constant evaluation: evaluated: 5, effectively constant: 2
|
|
@ -112,6 +112,10 @@ class ConstantsBackend {
|
||||||
/// Number semantics to use for this backend.
|
/// Number semantics to use for this backend.
|
||||||
NumberSemantics get numberSemantics => NumberSemantics.vm;
|
NumberSemantics get numberSemantics => NumberSemantics.vm;
|
||||||
|
|
||||||
|
/// If true, all constants are inlined. Otherwise [shouldInlineConstant] is
|
||||||
|
/// called to determine whether a constant expression should be inlined.
|
||||||
|
bool get alwaysInlineConstants => true;
|
||||||
|
|
||||||
/// Inline control of constant variables. The given constant expression
|
/// Inline control of constant variables. The given constant expression
|
||||||
/// is the initializer of a [Field] or [VariableDeclaration] node.
|
/// is the initializer of a [Field] or [VariableDeclaration] node.
|
||||||
/// If this method returns `true`, the variable will be inlined at all
|
/// If this method returns `true`, the variable will be inlined at all
|
||||||
|
@ -119,7 +123,11 @@ class ConstantsBackend {
|
||||||
/// by the `keepFields` or `keepLocals` properties).
|
/// by the `keepFields` or `keepLocals` properties).
|
||||||
/// This method must be deterministic, i.e. it must always return the same
|
/// This method must be deterministic, i.e. it must always return the same
|
||||||
/// value for the same constant value and place in the AST.
|
/// value for the same constant value and place in the AST.
|
||||||
bool shouldInlineConstant(ConstantExpression initializer) => true;
|
///
|
||||||
|
/// This is only called if [alwaysInlineConstants] is `true`.
|
||||||
|
bool shouldInlineConstant(ConstantExpression initializer) =>
|
||||||
|
throw new UnsupportedError(
|
||||||
|
'Per-value constant inlining is not supported');
|
||||||
|
|
||||||
/// Whether this target supports unevaluated constants.
|
/// Whether this target supports unevaluated constants.
|
||||||
///
|
///
|
||||||
|
@ -445,7 +453,7 @@ abstract class Target {
|
||||||
Class? concreteDoubleLiteralClass(CoreTypes coreTypes, double value) => null;
|
Class? concreteDoubleLiteralClass(CoreTypes coreTypes, double value) => null;
|
||||||
Class? concreteStringLiteralClass(CoreTypes coreTypes, String value) => null;
|
Class? concreteStringLiteralClass(CoreTypes coreTypes, String value) => null;
|
||||||
|
|
||||||
ConstantsBackend constantsBackend(CoreTypes coreTypes);
|
ConstantsBackend get constantsBackend;
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoneConstantsBackend extends ConstantsBackend {
|
class NoneConstantsBackend extends ConstantsBackend {
|
||||||
|
@ -517,7 +525,7 @@ class NoneTarget extends Target {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConstantsBackend constantsBackend(CoreTypes coreTypes) =>
|
ConstantsBackend get constantsBackend =>
|
||||||
// TODO(johnniwinther): Should this vary with the use case?
|
// TODO(johnniwinther): Should this vary with the use case?
|
||||||
const NoneConstantsBackend(supportsUnevaluatedConstants: true);
|
const NoneConstantsBackend(supportsUnevaluatedConstants: true);
|
||||||
}
|
}
|
||||||
|
@ -763,9 +771,7 @@ class TargetWrapper extends Target {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConstantsBackend constantsBackend(CoreTypes coreTypes) {
|
ConstantsBackend get constantsBackend => _target.constantsBackend;
|
||||||
return _target.constantsBackend(coreTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool enableNative(Uri uri) {
|
bool enableNative(Uri uri) {
|
||||||
|
|
|
@ -8,9 +8,12 @@ import 'ast.dart';
|
||||||
import 'transformations/flags.dart';
|
import 'transformations/flags.dart';
|
||||||
import 'type_environment.dart' show StatefulStaticTypeContext, TypeEnvironment;
|
import 'type_environment.dart' show StatefulStaticTypeContext, TypeEnvironment;
|
||||||
|
|
||||||
void verifyComponent(Component component, {bool? isOutline, bool? afterConst}) {
|
void verifyComponent(Component component,
|
||||||
|
{bool? isOutline, bool? afterConst, bool constantsAreAlwaysInlined: true}) {
|
||||||
VerifyingVisitor.check(component,
|
VerifyingVisitor.check(component,
|
||||||
isOutline: isOutline, afterConst: afterConst);
|
isOutline: isOutline,
|
||||||
|
afterConst: afterConst,
|
||||||
|
constantsAreAlwaysInlined: constantsAreAlwaysInlined);
|
||||||
}
|
}
|
||||||
|
|
||||||
class VerificationError {
|
class VerificationError {
|
||||||
|
@ -67,6 +70,9 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
||||||
/// a verification error for anything that should have been removed by it.
|
/// a verification error for anything that should have been removed by it.
|
||||||
final bool afterConst;
|
final bool afterConst;
|
||||||
|
|
||||||
|
/// If true, constant fields and local variables are expected to be inlined.
|
||||||
|
final bool constantsAreAlwaysInlined;
|
||||||
|
|
||||||
AsyncMarker currentAsyncMarker = AsyncMarker.Sync;
|
AsyncMarker currentAsyncMarker = AsyncMarker.Sync;
|
||||||
|
|
||||||
bool inCatchBlock = false;
|
bool inCatchBlock = false;
|
||||||
|
@ -88,12 +94,20 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
||||||
TreeNode? get currentClassOrExtensionOrMember =>
|
TreeNode? get currentClassOrExtensionOrMember =>
|
||||||
currentMember ?? currentClass ?? currentExtension;
|
currentMember ?? currentClass ?? currentExtension;
|
||||||
|
|
||||||
static void check(Component component, {bool? isOutline, bool? afterConst}) {
|
static void check(Component component,
|
||||||
component.accept(
|
{bool? isOutline,
|
||||||
new VerifyingVisitor(isOutline: isOutline, afterConst: afterConst));
|
bool? afterConst,
|
||||||
|
required bool constantsAreAlwaysInlined}) {
|
||||||
|
component.accept(new VerifyingVisitor(
|
||||||
|
isOutline: isOutline,
|
||||||
|
afterConst: afterConst,
|
||||||
|
constantsAreAlwaysInlined: constantsAreAlwaysInlined));
|
||||||
}
|
}
|
||||||
|
|
||||||
VerifyingVisitor({bool? isOutline, bool? afterConst})
|
VerifyingVisitor(
|
||||||
|
{bool? isOutline,
|
||||||
|
bool? afterConst,
|
||||||
|
required this.constantsAreAlwaysInlined})
|
||||||
: isOutline = isOutline ?? false,
|
: isOutline = isOutline ?? false,
|
||||||
afterConst = afterConst ?? !(isOutline ?? false);
|
afterConst = afterConst ?? !(isOutline ?? false);
|
||||||
|
|
||||||
|
@ -577,10 +591,12 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
||||||
declareVariable(node);
|
declareVariable(node);
|
||||||
if (afterConst && node.isConst) {
|
if (afterConst && node.isConst) {
|
||||||
Expression? initializer = node.initializer;
|
Expression? initializer = node.initializer;
|
||||||
if (!(initializer is InvalidExpression ||
|
if (constantsAreAlwaysInlined) {
|
||||||
initializer is ConstantExpression &&
|
if (!(initializer is InvalidExpression ||
|
||||||
initializer.constant is UnevaluatedConstant)) {
|
initializer is ConstantExpression &&
|
||||||
problem(node, "Constant VariableDeclaration");
|
initializer.constant is UnevaluatedConstant)) {
|
||||||
|
problem(node, "Constant VariableDeclaration");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -589,7 +605,7 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
||||||
void visitVariableGet(VariableGet node) {
|
void visitVariableGet(VariableGet node) {
|
||||||
checkVariableInScope(node.variable, node);
|
checkVariableInScope(node.variable, node);
|
||||||
visitChildren(node);
|
visitChildren(node);
|
||||||
if (afterConst && node.variable.isConst) {
|
if (constantsAreAlwaysInlined && afterConst && node.variable.isConst) {
|
||||||
problem(node, "VariableGet of const variable '${node.variable}'.");
|
problem(node, "VariableGet of const variable '${node.variable}'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -621,7 +637,10 @@ class VerifyingVisitor extends RecursiveResultVisitor<void> {
|
||||||
if (node.target.isInstanceMember) {
|
if (node.target.isInstanceMember) {
|
||||||
problem(node, "StaticGet of '${node.target}' that's an instance member.");
|
problem(node, "StaticGet of '${node.target}' that's an instance member.");
|
||||||
}
|
}
|
||||||
if (afterConst && node.target is Field && node.target.isConst) {
|
if (constantsAreAlwaysInlined &&
|
||||||
|
afterConst &&
|
||||||
|
node.target is Field &&
|
||||||
|
node.target.isConst) {
|
||||||
problem(node, "StaticGet of const field '${node.target}'.");
|
problem(node, "StaticGet of const field '${node.target}'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -488,7 +488,7 @@ class VmTarget extends Target {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConstantsBackend constantsBackend(CoreTypes coreTypes) => ConstantsBackend();
|
ConstantsBackend get constantsBackend => const ConstantsBackend();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, String> updateEnvironmentDefines(Map<String, String> map) {
|
Map<String, String> updateEnvironmentDefines(Map<String, String> map) {
|
||||||
|
|
Loading…
Reference in a new issue