mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 21:41:19 +00:00
Add tests for handling closures in LocalInitializers
BUG=https://github.com/dart-lang/sdk/issues/29887 R=sjindel@google.com Review-Url: https://codereview.chromium.org/2944433002 .
This commit is contained in:
parent
42fd6cfd77
commit
b1e629f078
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2017, 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.
|
||||
|
||||
library kernel.transformations.argument_extraction_for_redirecting;
|
||||
|
||||
import '../ast.dart'
|
||||
show
|
||||
Program,
|
||||
Constructor,
|
||||
RedirectingInitializer,
|
||||
Library,
|
||||
LocalInitializer,
|
||||
VariableDeclaration,
|
||||
VariableGet,
|
||||
Expression,
|
||||
NamedExpression;
|
||||
import '../core_types.dart' show CoreTypes;
|
||||
import '../visitor.dart' show Transformer;
|
||||
|
||||
Program transformProgram(CoreTypes coreTypes, Program program) {
|
||||
new ArgumentExtractionForRedirecting().visitProgram(program);
|
||||
return program;
|
||||
}
|
||||
|
||||
void transformLibraries(CoreTypes coreTypes, List<Library> libraries) {
|
||||
var transformer = new ArgumentExtractionForRedirecting();
|
||||
for (var library in libraries) {
|
||||
transformer.visitLibrary(library);
|
||||
}
|
||||
}
|
||||
|
||||
class ArgumentExtractionForRedirecting extends Transformer {
|
||||
visitConstructor(Constructor node) {
|
||||
if (node.initializers.length == 1 &&
|
||||
node.initializers[0] is RedirectingInitializer) {
|
||||
int index = 0;
|
||||
RedirectingInitializer redirectingInitializer = node.initializers[0];
|
||||
List<Expression> positionalArguments =
|
||||
redirectingInitializer.arguments.positional;
|
||||
List<NamedExpression> namedArguments =
|
||||
redirectingInitializer.arguments.named;
|
||||
for (int i = 0; i < positionalArguments.length; i++) {
|
||||
Expression argument = positionalArguments[i];
|
||||
VariableDeclaration extractedArgument =
|
||||
new VariableDeclaration("extracted#$index", initializer: argument);
|
||||
LocalInitializer initializer = new LocalInitializer(extractedArgument)
|
||||
..parent = node;
|
||||
node.initializers.insert(index++, initializer);
|
||||
positionalArguments[i] = new VariableGet(extractedArgument)
|
||||
..parent = redirectingInitializer.arguments;
|
||||
}
|
||||
for (int i = 0; i < namedArguments.length; i++) {
|
||||
Expression argument = namedArguments[i].value;
|
||||
VariableDeclaration extractedArgument =
|
||||
new VariableDeclaration("extracted#$index", initializer: argument);
|
||||
LocalInitializer initializer = new LocalInitializer(extractedArgument)
|
||||
..parent = node;
|
||||
node.initializers.insert(index++, initializer);
|
||||
namedArguments[i].value = new VariableGet(extractedArgument)
|
||||
..parent = redirectingInitializer.arguments;
|
||||
}
|
||||
}
|
||||
return super.visitConstructor(node);
|
||||
}
|
||||
}
|
|
@ -70,7 +70,13 @@ import 'context.dart' show Context, NoContext;
|
|||
|
||||
import 'info.dart' show ClosureInfo;
|
||||
|
||||
import 'rewriter.dart' show AstRewriter, BlockRewriter, InitializerRewriter;
|
||||
import 'rewriter.dart'
|
||||
show
|
||||
AstRewriter,
|
||||
BlockRewriter,
|
||||
InitializerRewriter,
|
||||
FieldInitializerRewriter,
|
||||
LocalInitializerRewriter;
|
||||
|
||||
class ClosureConverter extends Transformer {
|
||||
final CoreTypes coreTypes;
|
||||
|
@ -195,18 +201,47 @@ class ClosureConverter extends Transformer {
|
|||
context.extend(parameter, new VariableGet(parameter));
|
||||
}
|
||||
|
||||
static InitializerRewriter getRewriterForInitializer(
|
||||
Initializer initializer) {
|
||||
if (initializer is FieldInitializer) {
|
||||
return new FieldInitializerRewriter(initializer.value);
|
||||
}
|
||||
if (initializer is LocalInitializer) {
|
||||
return new LocalInitializerRewriter(initializer.variable.initializer);
|
||||
}
|
||||
throw "Trying to extract an initializer expression from "
|
||||
"${initializer.runtimeType}, but only FieldInitializer and "
|
||||
"LocalInitializer are supported.";
|
||||
}
|
||||
|
||||
static Expression getInitializerExpression(Initializer initializer) {
|
||||
if (initializer is FieldInitializer) {
|
||||
return initializer.value;
|
||||
}
|
||||
if (initializer is LocalInitializer) {
|
||||
return initializer.variable.initializer;
|
||||
}
|
||||
throw "Trying to get initializing expressino from "
|
||||
"${initializer.runtimeType}, but only Field Initializer and "
|
||||
"LocalInitializer are supported.";
|
||||
}
|
||||
|
||||
TreeNode visitConstructor(Constructor node) {
|
||||
assert(isEmptyContext);
|
||||
currentMember = node;
|
||||
// Transform initializers.
|
||||
for (Initializer initializer in node.initializers) {
|
||||
if (initializer is FieldInitializer) {
|
||||
if (initializer is FieldInitializer || initializer is LocalInitializer) {
|
||||
// Create a rewriter and a context for the initializer expression.
|
||||
rewriter = new InitializerRewriter(initializer.value);
|
||||
InitializerRewriter initializerRewriter =
|
||||
getRewriterForInitializer(initializer);
|
||||
rewriter = initializerRewriter;
|
||||
context = new NoContext(this);
|
||||
// Save the expression to visit it in the extended context, since the
|
||||
// rewriter will modify `initializer.value`.
|
||||
Expression initializerExpression = initializer.value;
|
||||
// rewriter will modify `initializer.value` (for [FieldInitializer]) or
|
||||
// `initializer.variable.initializer` (for [LocalInitializer]).
|
||||
Expression initializerExpression =
|
||||
getInitializerExpression(initializer);
|
||||
// Extend the context with all captured parameters of the constructor.
|
||||
// TODO(karlklose): add a fine-grained analysis of captured parameters.
|
||||
node.function.positionalParameters
|
||||
|
@ -223,9 +258,11 @@ class ClosureConverter extends Transformer {
|
|||
parent.body = initializerExpression;
|
||||
} else if (parent is FieldInitializer) {
|
||||
parent.value = initializerExpression;
|
||||
} else if (parent is LocalInitializer) {
|
||||
parent.variable.initializer = initializerExpression;
|
||||
} else {
|
||||
throw "Found unexpected node '${node.runtimeType}, expected a 'Let' "
|
||||
"or a 'FieldInitializer'.";
|
||||
",a 'FieldInitializer', or a 'LocalInitializer'.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,12 +87,10 @@ class BlockRewriter extends AstRewriter {
|
|||
|
||||
/// Creates and updates the context as [Let] bindings around the initializer
|
||||
/// expression.
|
||||
class InitializerRewriter extends AstRewriter {
|
||||
abstract class InitializerRewriter extends AstRewriter {
|
||||
final Expression initializingExpression;
|
||||
|
||||
InitializerRewriter(this.initializingExpression) {
|
||||
assert(initializingExpression.parent is FieldInitializer);
|
||||
}
|
||||
InitializerRewriter(this.initializingExpression);
|
||||
|
||||
@override
|
||||
BlockRewriter forNestedBlock(Block block) {
|
||||
|
@ -102,11 +100,9 @@ class InitializerRewriter extends AstRewriter {
|
|||
@override
|
||||
void insertContextDeclaration(Expression accessParent) {
|
||||
_createDeclaration();
|
||||
FieldInitializer parent = initializingExpression.parent;
|
||||
Let binding = new Let(contextDeclaration, initializingExpression);
|
||||
setInitializerExpression(binding);
|
||||
initializingExpression.parent = binding;
|
||||
parent.value = binding;
|
||||
binding.parent = parent;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -117,4 +113,34 @@ class InitializerRewriter extends AstRewriter {
|
|||
parent.body = binding;
|
||||
binding.parent = parent;
|
||||
}
|
||||
|
||||
void setInitializerExpression(Expression expression);
|
||||
}
|
||||
|
||||
class FieldInitializerRewriter extends InitializerRewriter {
|
||||
FieldInitializerRewriter(Expression initializingExpression)
|
||||
: super(initializingExpression) {
|
||||
assert(initializingExpression.parent is FieldInitializer);
|
||||
}
|
||||
|
||||
void setInitializerExpression(Expression expression) {
|
||||
assert(initializingExpression.parent is FieldInitializer);
|
||||
FieldInitializer parent = initializingExpression.parent;
|
||||
parent.value = expression;
|
||||
expression.parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
class LocalInitializerRewriter extends InitializerRewriter {
|
||||
LocalInitializerRewriter(Expression initializingExpression)
|
||||
: super(initializingExpression) {
|
||||
assert(initializingExpression.parent is LocalInitializer);
|
||||
}
|
||||
|
||||
void setInitializerExpression(Expression expression) {
|
||||
assert(initializingExpression.parent is LocalInitializer);
|
||||
LocalInitializer parent = initializingExpression.parent;
|
||||
parent.variable.initializer = expression;
|
||||
expression.parent = parent;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# Copyright (c) 2017, 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.md file.
|
||||
|
||||
local_initializers: Crash
|
172
pkg/kernel/test/closures_initializers/suite.dart
Normal file
172
pkg/kernel/test/closures_initializers/suite.dart
Normal file
|
@ -0,0 +1,172 @@
|
|||
// Copyright (c) 2017, 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.md file.
|
||||
|
||||
library test.kernel.closures.suite;
|
||||
|
||||
import 'dart:async' show Future;
|
||||
|
||||
import 'package:front_end/physical_file_system.dart' show PhysicalFileSystem;
|
||||
|
||||
import 'package:kernel/core_types.dart' show CoreTypes;
|
||||
|
||||
import 'package:testing/testing.dart'
|
||||
show Chain, ChainContext, Result, Step, TestDescription, runMe;
|
||||
|
||||
import 'package:front_end/src/fasta/testing/patched_sdk_location.dart'
|
||||
show computePatchedSdk;
|
||||
|
||||
import 'package:kernel/ast.dart' show Program, Library;
|
||||
|
||||
import 'package:kernel/transformations/argument_extraction_for_redirecting.dart'
|
||||
as argument_extraction;
|
||||
|
||||
import 'package:kernel/transformations/closure_conversion.dart'
|
||||
as closure_conversion;
|
||||
|
||||
import 'package:front_end/src/fasta/testing/kernel_chain.dart'
|
||||
show Print, MatchExpectation, WriteDill, ReadDill, Verify;
|
||||
|
||||
import 'package:front_end/src/fasta/ticker.dart' show Ticker;
|
||||
|
||||
import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget;
|
||||
|
||||
import 'package:front_end/src/fasta/kernel/kernel_target.dart'
|
||||
show KernelTarget;
|
||||
|
||||
import 'package:front_end/src/fasta/translate_uri.dart' show TranslateUri;
|
||||
|
||||
import 'package:front_end/src/fasta/errors.dart' show InputError;
|
||||
|
||||
import 'package:front_end/src/fasta/testing/patched_sdk_location.dart';
|
||||
|
||||
import 'package:kernel/kernel.dart' show loadProgramFromBinary;
|
||||
|
||||
import 'package:kernel/target/targets.dart' show TargetFlags;
|
||||
|
||||
import 'package:kernel/target/vm_fasta.dart' show VmFastaTarget;
|
||||
|
||||
const String STRONG_MODE = " strong mode ";
|
||||
|
||||
class ClosureConversionContext extends ChainContext {
|
||||
final bool strongMode;
|
||||
|
||||
final TranslateUri uriTranslator;
|
||||
|
||||
final List<Step> steps;
|
||||
|
||||
ClosureConversionContext(
|
||||
this.strongMode, bool updateExpectations, this.uriTranslator)
|
||||
: steps = <Step>[
|
||||
const FastaCompile(),
|
||||
const Print(),
|
||||
const Verify(true),
|
||||
const ArgumentExtraction(),
|
||||
const Print(),
|
||||
const Verify(true),
|
||||
const ClosureConversion(),
|
||||
const Print(),
|
||||
const Verify(true),
|
||||
new MatchExpectation(".expect",
|
||||
updateExpectations: updateExpectations),
|
||||
const WriteDill(),
|
||||
const ReadDill(),
|
||||
// TODO(29143): add `Run` step when Vectors are added to VM.
|
||||
];
|
||||
|
||||
Future<Program> loadPlatform() async {
|
||||
Uri sdk = await computePatchedSdk();
|
||||
return loadProgramFromBinary(sdk.resolve('platform.dill').toFilePath());
|
||||
}
|
||||
|
||||
static Future<ClosureConversionContext> create(
|
||||
Chain suite, Map<String, String> environment) async {
|
||||
Uri sdk = await computePatchedSdk();
|
||||
Uri packages = Uri.base.resolve(".packages");
|
||||
bool strongMode = environment.containsKey(STRONG_MODE);
|
||||
bool updateExpectations = environment["updateExpectations"] == "true";
|
||||
TranslateUri uriTranslator = await TranslateUri
|
||||
.parse(PhysicalFileSystem.instance, sdk, packages: packages);
|
||||
return new ClosureConversionContext(
|
||||
strongMode, updateExpectations, uriTranslator);
|
||||
}
|
||||
}
|
||||
|
||||
Future<ClosureConversionContext> createContext(
|
||||
Chain suite, Map<String, String> environment) async {
|
||||
environment["updateExpectations"] =
|
||||
const String.fromEnvironment("updateExpectations");
|
||||
return ClosureConversionContext.create(suite, environment);
|
||||
}
|
||||
|
||||
class FastaCompile
|
||||
extends Step<TestDescription, Program, ClosureConversionContext> {
|
||||
const FastaCompile();
|
||||
|
||||
String get name => "fasta compilation";
|
||||
|
||||
Future<Result<Program>> run(
|
||||
TestDescription description, ClosureConversionContext context) async {
|
||||
Program platform = await context.loadPlatform();
|
||||
Ticker ticker = new Ticker();
|
||||
DillTarget dillTarget = new DillTarget(ticker, context.uriTranslator,
|
||||
new VmFastaTarget(new TargetFlags(strongMode: context.strongMode)));
|
||||
platform.unbindCanonicalNames();
|
||||
dillTarget.loader.appendLibraries(platform);
|
||||
KernelTarget sourceTarget = new KernelTarget(
|
||||
PhysicalFileSystem.instance, dillTarget, context.uriTranslator);
|
||||
|
||||
Program p;
|
||||
try {
|
||||
sourceTarget.read(description.uri);
|
||||
await dillTarget.buildOutlines();
|
||||
await sourceTarget.buildOutlines();
|
||||
p = await sourceTarget.buildProgram();
|
||||
} on InputError catch (e, s) {
|
||||
return fail(null, e.error, s);
|
||||
}
|
||||
return pass(p);
|
||||
}
|
||||
}
|
||||
|
||||
class ArgumentExtraction
|
||||
extends Step<Program, Program, ClosureConversionContext> {
|
||||
const ArgumentExtraction();
|
||||
|
||||
String get name => "argument extraction";
|
||||
|
||||
Future<Result<Program>> run(
|
||||
Program program, ClosureConversionContext context) async {
|
||||
try {
|
||||
CoreTypes coreTypes = new CoreTypes(program);
|
||||
Library library = program.libraries
|
||||
.firstWhere((Library library) => library.importUri.scheme != "dart");
|
||||
argument_extraction.transformLibraries(coreTypes, <Library>[library]);
|
||||
return pass(program);
|
||||
} catch (e, s) {
|
||||
return crash(e, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ClosureConversion
|
||||
extends Step<Program, Program, ClosureConversionContext> {
|
||||
const ClosureConversion();
|
||||
|
||||
String get name => "closure conversion";
|
||||
|
||||
Future<Result<Program>> run(
|
||||
Program program, ClosureConversionContext testContext) async {
|
||||
try {
|
||||
CoreTypes coreTypes = new CoreTypes(program);
|
||||
Library library = program.libraries
|
||||
.firstWhere((Library library) => library.importUri.scheme != "dart");
|
||||
closure_conversion.transformLibraries(coreTypes, <Library>[library]);
|
||||
return pass(program);
|
||||
} catch (e, s) {
|
||||
return crash(e, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main(List<String> arguments) => runMe(arguments, createContext, "testing.json");
|
28
pkg/kernel/test/closures_initializers/testing.json
Normal file
28
pkg/kernel/test/closures_initializers/testing.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"":"Copyright (c) 2017, 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.md file.",
|
||||
"packages": "../../../../.packages",
|
||||
"suites": [
|
||||
{
|
||||
"name": "closures_initializers",
|
||||
"kind": "Chain",
|
||||
"source": "suite.dart",
|
||||
"path": "../../testcases/closures_initializers/",
|
||||
"status": "closures_initializers.status",
|
||||
"pattern": [
|
||||
"\\.dart$"
|
||||
],
|
||||
"exclude": [
|
||||
"/test/closures_initializers/suite\\.dart$"
|
||||
]
|
||||
}
|
||||
],
|
||||
"analyze": {
|
||||
"uris": [
|
||||
"suite.dart"
|
||||
],
|
||||
"exclude": [
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2017, 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.
|
||||
|
||||
// The purpose of this test is to detect that closures in [LocalInitializer]s
|
||||
// are properly converted. This test assumes that
|
||||
// [ArgumentExtractionForRedirecting] transformer was run before closure
|
||||
// conversion. It should introduce one [LocalInitializer] for each argument
|
||||
// passed to the redirecting constructor. If such argument contains a closure,
|
||||
// it would appear in a [LocalInitializer].
|
||||
|
||||
class X {}
|
||||
|
||||
class A {
|
||||
X foo;
|
||||
A.named(X foo) {}
|
||||
A(X foo) : this.named((() => foo)());
|
||||
}
|
||||
|
||||
main() {
|
||||
A a = new A(new X());
|
||||
a.foo; // To prevent dartanalyzer from marking [a] as unused.
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class X extends core::Object {
|
||||
constructor •() → void
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
class A extends core::Object {
|
||||
field self::X foo = null;
|
||||
constructor named(self::X foo) → void
|
||||
: super core::Object::•() {}
|
||||
constructor •(self::X foo) → void
|
||||
: dynamic extracted#0 = () → self::X
|
||||
throw "Calling unconverted closure at pkg/kernel/testcases/closures_initializers/local_initializers.dart:17:26";
|
||||
.call(), this self::A::named(extracted#0)
|
||||
;
|
||||
}
|
||||
static method main() → dynamic {
|
||||
self::A a = new self::A::•(new self::X::•());
|
||||
a.foo;
|
||||
}
|
Loading…
Reference in a new issue