mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 15:29:45 +00:00
Merge kernel closure conversion into the Dart SDK
This is the result of: - taking the diff of the branch closure_conversion to master in the kernel repository - updating the file paths - applying the diff to the Dart SDK - fixing conflicts between the changes to pkg/kernel in the Dart SDK and the master branch in the kernel repository R=asgerf@google.com Review-Url: https://codereview.chromium.org/2561723003 .
This commit is contained in:
parent
f7b6f0cf1c
commit
ff99a0ce59
|
@ -89,6 +89,7 @@ stack_trace:third_party/pkg/stack_trace/lib
|
|||
stream_channel:third_party/pkg/stream_channel/lib
|
||||
string_scanner:third_party/pkg/string_scanner/lib
|
||||
test:third_party/pkg/test/lib
|
||||
testing:third_party/testing/lib
|
||||
test_reflective_loader:third_party/pkg/test_reflective_loader/lib
|
||||
typed_data:third_party/pkg/typed_data/lib
|
||||
typed_mock:pkg/typed_mock/lib
|
||||
|
|
5
DEPS
5
DEPS
|
@ -27,6 +27,8 @@ vars = {
|
|||
# Only use this temporarily while waiting for a mirror for a new package.
|
||||
"github_dartlang": "https://github.com/dart-lang/%s.git",
|
||||
|
||||
"github_testing": "https://github.com/peter-ahe-google/testing.git",
|
||||
|
||||
"gyp_rev": "@6ee91ad8659871916f9aa840d42e1513befdf638",
|
||||
"co19_rev": "@f05d5aee5930bfd487aedf832fbd7b832f502b15",
|
||||
|
||||
|
@ -109,6 +111,7 @@ vars = {
|
|||
"stream_channel_tag": "@1.5.0",
|
||||
"string_scanner_tag": "@1.0.0",
|
||||
"sunflower_rev": "@879b704933413414679396b129f5dfa96f7a0b1e",
|
||||
"testing_rev": "@2e196d51c147411a93a949109656be93626bda49",
|
||||
"test_reflective_loader_tag": "@0.1.0",
|
||||
"test_tag": "@0.12.15+6",
|
||||
"typed_data_tag": "@1.1.3",
|
||||
|
@ -315,6 +318,8 @@ deps = {
|
|||
Var("sunflower_rev"),
|
||||
Var("dart_root") + "/third_party/pkg/test":
|
||||
(Var("github_mirror") % "test") + Var("test_tag"),
|
||||
Var("dart_root") + "/third_party/testing":
|
||||
Var("github_testing") + Var("testing_rev"),
|
||||
Var("dart_root") + "/third_party/pkg/test_reflective_loader":
|
||||
(Var("github_mirror") % "test_reflective_loader") +
|
||||
Var("test_reflective_loader_tag"),
|
||||
|
|
|
@ -7,7 +7,10 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
enum CompilerOutcome { Ok, Fail, }
|
||||
enum CompilerOutcome {
|
||||
Ok,
|
||||
Fail,
|
||||
}
|
||||
|
||||
typedef Future<CompilerOutcome> BatchCallback(List<String> arguments);
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:kernel/kernel.dart';
|
|||
import 'package:kernel/transformations/continuation.dart' as cont;
|
||||
import 'package:kernel/transformations/infer_values.dart' as infer_values;
|
||||
import 'package:kernel/transformations/mixin_full_resolution.dart' as mix;
|
||||
import 'package:kernel/transformations/closure_conversion.dart' as closures;
|
||||
import 'package:kernel/transformations/treeshaker.dart' as treeshaker;
|
||||
|
||||
import 'batch_util.dart';
|
||||
|
@ -72,6 +73,9 @@ Future<CompilerOutcome> runTransformation(List<String> arguments) async {
|
|||
case 'resolve-mixins':
|
||||
program = mix.transformProgram(program);
|
||||
break;
|
||||
case 'closures':
|
||||
program = closures.transformProgram(program);
|
||||
break;
|
||||
case 'treeshake':
|
||||
program = treeshaker.transformProgram(program);
|
||||
break;
|
||||
|
|
|
@ -342,7 +342,7 @@ class Class extends TreeNode {
|
|||
this.supertype,
|
||||
this.mixedInType,
|
||||
List<TypeParameter> typeParameters,
|
||||
List<InterfaceType> implementedTypes,
|
||||
List<Supertype> implementedTypes,
|
||||
List<Constructor> constructors,
|
||||
List<Procedure> procedures,
|
||||
List<Field> fields,
|
||||
|
|
|
@ -551,8 +551,7 @@ class BinaryBuilder {
|
|||
readExpression(), readMemberReference(), readExpression());
|
||||
case Tag.StaticGet:
|
||||
int offset = readOffset();
|
||||
return new StaticGet(readMemberReference())
|
||||
..fileOffset = offset;
|
||||
return new StaticGet(readMemberReference())..fileOffset = offset;
|
||||
case Tag.StaticSet:
|
||||
return new StaticSet(readMemberReference(), readExpression());
|
||||
case Tag.MethodInvocation:
|
||||
|
|
|
@ -365,6 +365,7 @@ class ClassHierarchy {
|
|||
inherited = _getUnshadowedInheritedMembers(declared, inherited);
|
||||
allInheritedMembers = _merge(allInheritedMembers, inherited);
|
||||
}
|
||||
|
||||
inheritFrom(classNode.supertype);
|
||||
inheritFrom(classNode.mixedInType);
|
||||
classNode.implementedTypes.forEach(inheritFrom);
|
||||
|
|
|
@ -374,11 +374,13 @@ class CloneVisitor extends TreeVisitor {
|
|||
return newNode;
|
||||
}
|
||||
|
||||
TreeNode cloneFunctionNodeBody(FunctionNode node) => cloneOptional(node.body);
|
||||
|
||||
visitFunctionNode(FunctionNode node) {
|
||||
var typeParameters = node.typeParameters.map(clone).toList();
|
||||
var positional = node.positionalParameters.map(clone).toList();
|
||||
var named = node.namedParameters.map(clone).toList();
|
||||
return new FunctionNode(cloneOptional(node.body),
|
||||
return new FunctionNode(cloneFunctionNodeBody(node),
|
||||
typeParameters: typeParameters,
|
||||
positionalParameters: positional,
|
||||
namedParameters: named,
|
||||
|
|
|
@ -33,10 +33,10 @@ void moveSuperInitializerLast(Constructor node) {
|
|||
// [LocalInitializer]s.
|
||||
initializers.length += argumentCount;
|
||||
initializers.setRange(
|
||||
superIndex + argumentCount, // desination start (inclusive)
|
||||
initializers.length - 1, // desination end (exclusive)
|
||||
initializers, // source list
|
||||
superIndex + 1); // source start index
|
||||
superIndex + argumentCount, // desination start (inclusive)
|
||||
initializers.length - 1, // desination end (exclusive)
|
||||
initializers, // source list
|
||||
superIndex + 1); // source start index
|
||||
initializers[initializers.length - 1] = superCall;
|
||||
|
||||
// Fill in the [argumentCount] reserved slots with the evaluation expressions
|
||||
|
|
|
@ -50,7 +50,8 @@ void writeLibraryToText(Library library, {String path}) {
|
|||
}
|
||||
}
|
||||
|
||||
void writeProgramToText(Program program, {String path, bool showExternal: false}) {
|
||||
void writeProgramToText(Program program,
|
||||
{String path, bool showExternal: false}) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
new Printer(buffer, showExternal: showExternal).writeProgramFile(program);
|
||||
if (path == null) {
|
||||
|
|
|
@ -56,7 +56,6 @@ class ExpressionLifter extends Transformer {
|
|||
/// [statements] to a fresh empty list before transforming those children.
|
||||
List<Statement> statements = <Statement>[];
|
||||
|
||||
|
||||
/// The number of currently live named intermediate values.
|
||||
///
|
||||
/// This index is used to allocate names to temporary values. Because
|
||||
|
@ -76,8 +75,7 @@ class ExpressionLifter extends Transformer {
|
|||
/// [nameIndex] may still account for names of subexpressions.
|
||||
int nameIndex = 0;
|
||||
|
||||
final VariableDeclaration asyncResult =
|
||||
new VariableDeclaration(':result');
|
||||
final VariableDeclaration asyncResult = new VariableDeclaration(':result');
|
||||
final List<VariableDeclaration> variables = <VariableDeclaration>[];
|
||||
|
||||
ExpressionLifter(this.continuationRewriter);
|
||||
|
@ -176,13 +174,11 @@ class ExpressionLifter extends Transformer {
|
|||
// children.
|
||||
var index = nameIndex;
|
||||
|
||||
|
||||
// 3. Transform the children. Initially they do not have an await in a
|
||||
// sibling to their right.
|
||||
seenAwait = false;
|
||||
action();
|
||||
|
||||
|
||||
// 4. If the expression was named then the variables used for children are
|
||||
// no longer live but the variable used for the expression is.
|
||||
if (shouldName) {
|
||||
|
@ -194,7 +190,9 @@ class ExpressionLifter extends Transformer {
|
|||
|
||||
// Unary expressions.
|
||||
Expression unary(Expression expr) {
|
||||
return transform(expr, () { expr.transformChildren(this); });
|
||||
return transform(expr, () {
|
||||
expr.transformChildren(this);
|
||||
});
|
||||
}
|
||||
|
||||
TreeNode visitVariableSet(VariableSet expr) => unary(expr);
|
||||
|
@ -249,15 +247,21 @@ class ExpressionLifter extends Transformer {
|
|||
}
|
||||
|
||||
TreeNode visitSuperMethodInvocation(SuperMethodInvocation expr) {
|
||||
return transform(expr, () { visitArguments(expr.arguments); });
|
||||
return transform(expr, () {
|
||||
visitArguments(expr.arguments);
|
||||
});
|
||||
}
|
||||
|
||||
TreeNode visitStaticInvocation(StaticInvocation expr) {
|
||||
return transform(expr, () { visitArguments(expr.arguments); });
|
||||
return transform(expr, () {
|
||||
visitArguments(expr.arguments);
|
||||
});
|
||||
}
|
||||
|
||||
TreeNode visitConstructorInvocation(ConstructorInvocation expr) {
|
||||
return transform(expr, () { visitArguments(expr.arguments); });
|
||||
return transform(expr, () {
|
||||
visitArguments(expr.arguments);
|
||||
});
|
||||
}
|
||||
|
||||
TreeNode visitStringConcatenation(StringConcatenation expr) {
|
||||
|
@ -295,7 +299,7 @@ class ExpressionLifter extends Transformer {
|
|||
var rightStatements = <Statement>[];
|
||||
seenAwait = false;
|
||||
expr.right = delimit(() => expr.right.accept(this), rightStatements)
|
||||
..parent = expr;
|
||||
..parent = expr;
|
||||
var rightAwait = seenAwait;
|
||||
|
||||
if (rightStatements.isEmpty) {
|
||||
|
@ -321,13 +325,10 @@ class ExpressionLifter extends Transformer {
|
|||
// so they occur before in the corresponding block).
|
||||
var rightBody = blockOf(rightStatements);
|
||||
var result = allocateTemporary(nameIndex);
|
||||
rightBody.addStatement(new ExpressionStatement(
|
||||
new VariableSet(
|
||||
result,
|
||||
new MethodInvocation(
|
||||
expr.right,
|
||||
new Name('=='),
|
||||
new Arguments(<Expression>[new BoolLiteral(true)])))));
|
||||
rightBody.addStatement(new ExpressionStatement(new VariableSet(
|
||||
result,
|
||||
new MethodInvocation(expr.right, new Name('=='),
|
||||
new Arguments(<Expression>[new BoolLiteral(true)])))));
|
||||
var then, otherwise;
|
||||
if (expr.operator == '&&') {
|
||||
then = rightBody;
|
||||
|
@ -338,13 +339,9 @@ class ExpressionLifter extends Transformer {
|
|||
}
|
||||
statements.add(new IfStatement(new VariableGet(result), then, otherwise));
|
||||
|
||||
var test =
|
||||
new MethodInvocation(
|
||||
expr.left,
|
||||
new Name('=='),
|
||||
new Arguments(<Expression>[new BoolLiteral(true)]));
|
||||
statements.add(
|
||||
new ExpressionStatement(new VariableSet(result, test)));
|
||||
var test = new MethodInvocation(expr.left, new Name('=='),
|
||||
new Arguments(<Expression>[new BoolLiteral(true)]));
|
||||
statements.add(new ExpressionStatement(new VariableSet(result, test)));
|
||||
|
||||
seenAwait = false;
|
||||
test.receiver = test.receiver.accept(this)..parent = test;
|
||||
|
@ -362,13 +359,14 @@ class ExpressionLifter extends Transformer {
|
|||
var thenStatements = <Statement>[];
|
||||
seenAwait = false;
|
||||
expr.then = delimit(() => expr.then.accept(this), thenStatements)
|
||||
..parent = expr;
|
||||
..parent = expr;
|
||||
var thenAwait = seenAwait;
|
||||
|
||||
var otherwiseStatements = <Statement>[];
|
||||
seenAwait = false;
|
||||
expr.otherwise = delimit(() => expr.otherwise.accept(this),
|
||||
otherwiseStatements)..parent = expr;
|
||||
expr.otherwise =
|
||||
delimit(() => expr.otherwise.accept(this), otherwiseStatements)
|
||||
..parent = expr;
|
||||
var otherwiseAwait = seenAwait;
|
||||
|
||||
if (thenStatements.isEmpty && otherwiseStatements.isEmpty) {
|
||||
|
@ -416,9 +414,10 @@ class ExpressionLifter extends Transformer {
|
|||
if (shouldName) result = name(result);
|
||||
statements.add(R.createContinuationPoint());
|
||||
Arguments arguments = new Arguments(<Expression>[
|
||||
expr.operand,
|
||||
new VariableGet(R.thenContinuationVariable),
|
||||
new VariableGet(R.catchErrorContinuationVariable)]);
|
||||
expr.operand,
|
||||
new VariableGet(R.thenContinuationVariable),
|
||||
new VariableGet(R.catchErrorContinuationVariable)
|
||||
]);
|
||||
statements.add(new ExpressionStatement(
|
||||
new StaticInvocation(R.helper.awaitHelper, arguments)));
|
||||
|
||||
|
@ -461,8 +460,8 @@ class ExpressionLifter extends Transformer {
|
|||
statements.add(variable);
|
||||
var index = nameIndex;
|
||||
seenAwait = false;
|
||||
variable.initializer =
|
||||
variable.initializer.accept(this)..parent = variable;
|
||||
variable.initializer = variable.initializer.accept(this)
|
||||
..parent = variable;
|
||||
// Temporaries used in the initializer or the body are not live but the
|
||||
// temporary used for the body is.
|
||||
nameIndex = index + 1;
|
||||
|
@ -475,15 +474,15 @@ class ExpressionLifter extends Transformer {
|
|||
return transform(expr, () {
|
||||
// The body has already been translated.
|
||||
expr.body = body..parent = expr;
|
||||
variable.initializer =
|
||||
variable.initializer.accept(this)..parent = variable;
|
||||
variable.initializer = variable.initializer.accept(this)
|
||||
..parent = variable;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
visitFunctionNode(FunctionNode node) {
|
||||
var nestedRewriter = new RecursiveContinuationRewriter(
|
||||
continuationRewriter.helper);
|
||||
var nestedRewriter =
|
||||
new RecursiveContinuationRewriter(continuationRewriter.helper);
|
||||
return node.accept(nestedRewriter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library kernel.transformations.closure.converter;
|
||||
|
||||
import '../../ast.dart' show DartType, FunctionNode, TreeNode, TypeParameter;
|
||||
|
||||
import '../../clone.dart' show CloneVisitor;
|
||||
|
||||
class CloneWithoutBody extends CloneVisitor {
|
||||
CloneWithoutBody({Map<TypeParameter, DartType> typeSubstitution})
|
||||
: super(typeSubstitution: typeSubstitution);
|
||||
|
||||
@override
|
||||
TreeNode cloneFunctionNodeBody(FunctionNode node) => null;
|
||||
}
|
248
pkg/kernel/lib/transformations/closure/context.dart
Normal file
248
pkg/kernel/lib/transformations/closure/context.dart
Normal file
|
@ -0,0 +1,248 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library kernel.transformations.closure.context;
|
||||
|
||||
import '../../ast.dart'
|
||||
show
|
||||
Arguments,
|
||||
Class,
|
||||
ConstructorInvocation,
|
||||
Expression,
|
||||
ExpressionStatement,
|
||||
IntLiteral,
|
||||
InterfaceType,
|
||||
MethodInvocation,
|
||||
Name,
|
||||
NullLiteral,
|
||||
PropertyGet,
|
||||
PropertySet,
|
||||
StringLiteral,
|
||||
Throw,
|
||||
VariableDeclaration,
|
||||
VariableGet,
|
||||
VariableSet;
|
||||
|
||||
import '../../frontend/accessors.dart'
|
||||
show Accessor, IndexAccessor, VariableAccessor;
|
||||
|
||||
import 'converter.dart' show ClosureConverter;
|
||||
|
||||
abstract class Context {
|
||||
/// Returns a new expression for accessing this context.
|
||||
Expression get expression;
|
||||
|
||||
/// Returns an accessor (or null) for accessing this context.
|
||||
Accessor get accessor;
|
||||
|
||||
/// Extend the context to include [variable] initialized to [value]. For
|
||||
/// example, this replaces the [VariableDeclaration] node of a captured local
|
||||
/// variable.
|
||||
///
|
||||
/// This may create a new context and update the `context` field of the
|
||||
/// current [ClosureConverter].
|
||||
// TODO(ahe): Return context instead?
|
||||
void extend(VariableDeclaration variable, Expression value);
|
||||
|
||||
/// Update the initializer [value] of [variable] which was previously added
|
||||
/// with [extend]. This is used when [value] isn't available when the context
|
||||
/// was extended.
|
||||
void update(VariableDeclaration variable, Expression value) {
|
||||
throw "not supported $runtimeType";
|
||||
}
|
||||
|
||||
/// Returns a new expression for reading the value of [variable] from this
|
||||
/// context. For example, for replacing a [VariableGet] of a captured local
|
||||
/// variable.
|
||||
Expression lookup(VariableDeclaration variable);
|
||||
|
||||
/// Returns a new expression which stores [value] in [variable] in this
|
||||
/// context. For example, for replacing a [VariableSet] of a captured local
|
||||
/// variable.
|
||||
Expression assign(VariableDeclaration variable, Expression value,
|
||||
{bool voidContext: false});
|
||||
|
||||
/// Returns a new context whose parent is this context. The optional argument
|
||||
/// [accessor] controls how the nested context access this context. This is
|
||||
/// used, for example, when hoisting a local function. In this case, access
|
||||
/// to this context can't be accessed directly via [expression]. In other
|
||||
/// cases, for example, a for-loop, this context is still in scope and can be
|
||||
/// accessed directly (with [accessor]).
|
||||
Context toNestedContext([Accessor accessor]);
|
||||
|
||||
/// Returns a new expression which will copy this context and store the copy
|
||||
/// in the local variable currently holding this context.
|
||||
Expression clone() {
|
||||
return new Throw(
|
||||
new StringLiteral("Context clone not implemented for ${runtimeType}"));
|
||||
}
|
||||
}
|
||||
|
||||
class NoContext extends Context {
|
||||
final ClosureConverter converter;
|
||||
|
||||
NoContext(this.converter);
|
||||
|
||||
Expression get expression => new NullLiteral();
|
||||
|
||||
Accessor get accessor => null;
|
||||
|
||||
void extend(VariableDeclaration variable, Expression value) {
|
||||
converter.context = new LocalContext(converter, this)
|
||||
..extend(variable, value);
|
||||
}
|
||||
|
||||
Expression lookup(VariableDeclaration variable) {
|
||||
throw 'Unbound NoContext.lookup($variable)';
|
||||
}
|
||||
|
||||
Expression assign(VariableDeclaration variable, Expression value,
|
||||
{bool voidContext: false}) {
|
||||
throw 'Unbound NoContext.assign($variable, ...)';
|
||||
}
|
||||
|
||||
Context toNestedContext([Accessor accessor]) {
|
||||
return new NestedContext(
|
||||
converter, accessor, <List<VariableDeclaration>>[]);
|
||||
}
|
||||
}
|
||||
|
||||
class LocalContext extends Context {
|
||||
final ClosureConverter converter;
|
||||
final Context parent;
|
||||
final VariableDeclaration self;
|
||||
final IntLiteral size;
|
||||
final List<VariableDeclaration> variables = <VariableDeclaration>[];
|
||||
final Map<VariableDeclaration, Arguments> initializers =
|
||||
<VariableDeclaration, Arguments>{};
|
||||
|
||||
LocalContext._internal(this.converter, this.parent, this.self, this.size);
|
||||
|
||||
factory LocalContext(ClosureConverter converter, Context parent) {
|
||||
Class contextClass = converter.contextClass;
|
||||
assert(contextClass.constructors.length == 1);
|
||||
IntLiteral zero = new IntLiteral(0);
|
||||
VariableDeclaration declaration = new VariableDeclaration.forValue(
|
||||
new ConstructorInvocation(
|
||||
contextClass.constructors.first, new Arguments(<Expression>[zero])),
|
||||
type: new InterfaceType(contextClass));
|
||||
declaration.name = "#context";
|
||||
converter.insert(declaration);
|
||||
converter.insert(new ExpressionStatement(new PropertySet(
|
||||
new VariableGet(declaration), new Name('parent'), parent.expression)));
|
||||
|
||||
return new LocalContext._internal(converter, parent, declaration, zero);
|
||||
}
|
||||
|
||||
Expression get expression => accessor.buildSimpleRead();
|
||||
|
||||
Accessor get accessor => new VariableAccessor(self);
|
||||
|
||||
void extend(VariableDeclaration variable, Expression value) {
|
||||
Arguments arguments =
|
||||
new Arguments(<Expression>[new IntLiteral(variables.length), value]);
|
||||
converter.insert(new ExpressionStatement(
|
||||
new MethodInvocation(expression, new Name('[]='), arguments)));
|
||||
++size.value;
|
||||
variables.add(variable);
|
||||
initializers[variable] = arguments;
|
||||
}
|
||||
|
||||
void update(VariableDeclaration variable, Expression value) {
|
||||
Arguments arguments = initializers[variable];
|
||||
arguments.positional[1] = value;
|
||||
value.parent = arguments;
|
||||
}
|
||||
|
||||
Expression lookup(VariableDeclaration variable) {
|
||||
var index = variables.indexOf(variable);
|
||||
return index == -1
|
||||
? parent.lookup(variable)
|
||||
: new MethodInvocation(expression, new Name('[]'),
|
||||
new Arguments(<Expression>[new IntLiteral(index)]));
|
||||
}
|
||||
|
||||
Expression assign(VariableDeclaration variable, Expression value,
|
||||
{bool voidContext: false}) {
|
||||
var index = variables.indexOf(variable);
|
||||
return index == -1
|
||||
? parent.assign(variable, value, voidContext: voidContext)
|
||||
: IndexAccessor
|
||||
.make(expression, new IntLiteral(index), null, null)
|
||||
.buildAssignment(value, voidContext: voidContext);
|
||||
}
|
||||
|
||||
Context toNestedContext([Accessor accessor]) {
|
||||
accessor ??= this.accessor;
|
||||
List<List<VariableDeclaration>> variabless = <List<VariableDeclaration>>[];
|
||||
var current = this;
|
||||
while (current != null && current is! NoContext) {
|
||||
if (current is LocalContext) {
|
||||
variabless.add(current.variables);
|
||||
current = current.parent;
|
||||
} else if (current is NestedContext) {
|
||||
variabless.addAll((current as NestedContext).variabless);
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
return new NestedContext(converter, accessor, variabless);
|
||||
}
|
||||
|
||||
Expression clone() {
|
||||
self.isFinal = false;
|
||||
return new VariableSet(
|
||||
self,
|
||||
new MethodInvocation(
|
||||
new VariableGet(self), new Name("copy"), new Arguments.empty()));
|
||||
}
|
||||
}
|
||||
|
||||
class NestedContext extends Context {
|
||||
final ClosureConverter converter;
|
||||
final Accessor accessor;
|
||||
final List<List<VariableDeclaration>> variabless;
|
||||
|
||||
NestedContext(this.converter, this.accessor, this.variabless);
|
||||
|
||||
Expression get expression {
|
||||
return accessor?.buildSimpleRead() ?? new NullLiteral();
|
||||
}
|
||||
|
||||
void extend(VariableDeclaration variable, Expression value) {
|
||||
converter.context = new LocalContext(converter, this)
|
||||
..extend(variable, value);
|
||||
}
|
||||
|
||||
Expression lookup(VariableDeclaration variable) {
|
||||
var context = expression;
|
||||
for (var variables in variabless) {
|
||||
var index = variables.indexOf(variable);
|
||||
if (index != -1) {
|
||||
return new MethodInvocation(context, new Name('[]'),
|
||||
new Arguments(<Expression>[new IntLiteral(index)]));
|
||||
}
|
||||
context = new PropertyGet(context, new Name('parent'));
|
||||
}
|
||||
throw 'Unbound NestedContext.lookup($variable)';
|
||||
}
|
||||
|
||||
Expression assign(VariableDeclaration variable, Expression value,
|
||||
{bool voidContext: false}) {
|
||||
var context = expression;
|
||||
for (var variables in variabless) {
|
||||
var index = variables.indexOf(variable);
|
||||
if (index != -1) {
|
||||
return IndexAccessor
|
||||
.make(context, new IntLiteral(index), null, null)
|
||||
.buildAssignment(value, voidContext: voidContext);
|
||||
}
|
||||
context = new PropertyGet(context, new Name('parent'));
|
||||
}
|
||||
throw 'Unbound NestedContext.lookup($variable)';
|
||||
}
|
||||
|
||||
Context toNestedContext([Accessor accessor]) {
|
||||
return new NestedContext(converter, accessor ?? this.accessor, variabless);
|
||||
}
|
||||
}
|
792
pkg/kernel/lib/transformations/closure/converter.dart
Normal file
792
pkg/kernel/lib/transformations/closure/converter.dart
Normal file
|
@ -0,0 +1,792 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library kernel.transformations.closure.converter;
|
||||
|
||||
import '../../ast.dart'
|
||||
show
|
||||
Arguments,
|
||||
Block,
|
||||
Catch,
|
||||
Class,
|
||||
Constructor,
|
||||
ConstructorInvocation,
|
||||
DartType,
|
||||
EmptyStatement,
|
||||
Expression,
|
||||
ExpressionStatement,
|
||||
Field,
|
||||
FieldInitializer,
|
||||
ForInStatement,
|
||||
ForStatement,
|
||||
FunctionDeclaration,
|
||||
FunctionExpression,
|
||||
FunctionNode,
|
||||
InferredValue,
|
||||
Initializer,
|
||||
InvalidExpression,
|
||||
InvocationExpression,
|
||||
Library,
|
||||
LocalInitializer,
|
||||
Member,
|
||||
MethodInvocation,
|
||||
Name,
|
||||
NamedExpression,
|
||||
NullLiteral,
|
||||
Procedure,
|
||||
ProcedureKind,
|
||||
PropertyGet,
|
||||
ReturnStatement,
|
||||
Statement,
|
||||
StaticGet,
|
||||
StaticInvocation,
|
||||
StringLiteral,
|
||||
Supertype,
|
||||
ThisExpression,
|
||||
Transformer,
|
||||
TreeNode,
|
||||
TypeParameter,
|
||||
TypeParameterType,
|
||||
VariableDeclaration,
|
||||
VariableGet,
|
||||
VariableSet,
|
||||
transformList;
|
||||
|
||||
import '../../frontend/accessors.dart' show VariableAccessor;
|
||||
|
||||
import '../../clone.dart' show CloneVisitor;
|
||||
|
||||
import '../../core_types.dart' show CoreTypes;
|
||||
|
||||
import '../../type_algebra.dart' show substitute;
|
||||
|
||||
import 'clone_without_body.dart' show CloneWithoutBody;
|
||||
|
||||
import 'context.dart' show Context, NoContext;
|
||||
|
||||
import 'info.dart' show ClosureInfo;
|
||||
|
||||
class ClosureConverter extends Transformer {
|
||||
final CoreTypes coreTypes;
|
||||
final Class contextClass;
|
||||
final Set<VariableDeclaration> capturedVariables;
|
||||
final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables;
|
||||
final Map<FunctionNode, VariableDeclaration> thisAccess;
|
||||
final Map<FunctionNode, String> localNames;
|
||||
|
||||
/// Records place-holders for cloning contexts. See [visitForStatement].
|
||||
final Set<InvalidExpression> contextClonePlaceHolders =
|
||||
new Set<InvalidExpression>();
|
||||
|
||||
/// Maps the names of all instance methods that may be torn off (aka
|
||||
/// implicitly closurized) to `${name.name}#get`.
|
||||
final Map<Name, Name> tearOffGetterNames;
|
||||
|
||||
final CloneVisitor cloner = new CloneWithoutBody();
|
||||
|
||||
/// New members to add to [currentLibrary] after it has been
|
||||
/// transformed. These members will not be transformed themselves.
|
||||
final List<TreeNode> newLibraryMembers = <TreeNode>[];
|
||||
|
||||
/// New members to add to [currentClass] after it has been transformed. These
|
||||
/// members will not be transformed themselves.
|
||||
final List<Member> newClassMembers = <Member>[];
|
||||
|
||||
Library currentLibrary;
|
||||
|
||||
Class currentClass;
|
||||
|
||||
Member currentMember;
|
||||
|
||||
FunctionNode currentMemberFunction;
|
||||
|
||||
FunctionNode currentFunction;
|
||||
|
||||
Block _currentBlock;
|
||||
|
||||
int _insertionIndex = 0;
|
||||
|
||||
Context context;
|
||||
|
||||
/// Maps original type variable (aka type parameter) to a hoisted type
|
||||
/// variable type.
|
||||
///
|
||||
/// For example, consider:
|
||||
///
|
||||
/// class C<T> {
|
||||
/// f() => (x) => x is T;
|
||||
/// }
|
||||
///
|
||||
/// This is currently converted to:
|
||||
///
|
||||
/// class C<T> {
|
||||
/// f() => new Closure#0<T>();
|
||||
/// }
|
||||
/// class Closure#0<T_> implements Function {
|
||||
/// call(x) => x is T_;
|
||||
/// }
|
||||
///
|
||||
/// In this example, `typeSubstitution[T].parameter == T_` when transforming
|
||||
/// the closure in `f`.
|
||||
Map<TypeParameter, DartType> typeSubstitution =
|
||||
const <TypeParameter, DartType>{};
|
||||
|
||||
ClosureConverter(this.coreTypes, ClosureInfo info, this.contextClass)
|
||||
: this.capturedVariables = info.variables,
|
||||
this.capturedTypeVariables = info.typeVariables,
|
||||
this.thisAccess = info.thisAccess,
|
||||
this.localNames = info.localNames,
|
||||
this.tearOffGetterNames = info.tearOffGetterNames;
|
||||
|
||||
bool get isOuterMostContext {
|
||||
return currentFunction == null || currentMemberFunction == currentFunction;
|
||||
}
|
||||
|
||||
String get currentFileUri {
|
||||
if (currentMember is Constructor) return currentClass.fileUri;
|
||||
if (currentMember is Field) return (currentMember as Field).fileUri;
|
||||
if (currentMember is Procedure) return (currentMember as Procedure).fileUri;
|
||||
throw "No file uri";
|
||||
}
|
||||
|
||||
void insert(Statement statement) {
|
||||
_currentBlock.statements.insert(_insertionIndex++, statement);
|
||||
statement.parent = _currentBlock;
|
||||
}
|
||||
|
||||
TreeNode saveContext(TreeNode f()) {
|
||||
Block savedBlock = _currentBlock;
|
||||
int savedIndex = _insertionIndex;
|
||||
Context savedContext = context;
|
||||
try {
|
||||
return f();
|
||||
} finally {
|
||||
_currentBlock = savedBlock;
|
||||
_insertionIndex = savedIndex;
|
||||
context = savedContext;
|
||||
}
|
||||
}
|
||||
|
||||
TreeNode visitLibrary(Library node) {
|
||||
assert(newLibraryMembers.isEmpty);
|
||||
if (node == contextClass.enclosingLibrary) return node;
|
||||
currentLibrary = node;
|
||||
node = super.visitLibrary(node);
|
||||
for (TreeNode member in newLibraryMembers) {
|
||||
if (member is Class) {
|
||||
node.addClass(member);
|
||||
} else {
|
||||
node.addMember(member);
|
||||
}
|
||||
}
|
||||
newLibraryMembers.clear();
|
||||
currentLibrary = null;
|
||||
return node;
|
||||
}
|
||||
|
||||
TreeNode visitClass(Class node) {
|
||||
assert(newClassMembers.isEmpty);
|
||||
currentClass = node;
|
||||
node = super.visitClass(node);
|
||||
newClassMembers.forEach(node.addMember);
|
||||
newClassMembers.clear();
|
||||
currentClass = null;
|
||||
return node;
|
||||
}
|
||||
|
||||
TreeNode visitConstructor(Constructor node) {
|
||||
// TODO(ahe): Convert closures in constructors as well.
|
||||
return node;
|
||||
}
|
||||
|
||||
Expression handleLocalFunction(FunctionNode function) {
|
||||
FunctionNode enclosingFunction = currentFunction;
|
||||
Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution;
|
||||
currentFunction = function;
|
||||
Statement body = function.body;
|
||||
assert(body != null);
|
||||
|
||||
if (body is Block) {
|
||||
_currentBlock = body;
|
||||
} else {
|
||||
_currentBlock = new Block(<Statement>[body]);
|
||||
function.body = body.parent = _currentBlock;
|
||||
}
|
||||
_insertionIndex = 0;
|
||||
|
||||
VariableDeclaration contextVariable = new VariableDeclaration(
|
||||
"#contextParameter",
|
||||
type: contextClass.rawType,
|
||||
isFinal: true);
|
||||
Context parent = context;
|
||||
context = context.toNestedContext(new VariableAccessor(contextVariable));
|
||||
|
||||
Set<TypeParameter> captured = capturedTypeVariables[currentFunction];
|
||||
if (captured != null) {
|
||||
typeSubstitution = copyTypeVariables(captured);
|
||||
} else {
|
||||
typeSubstitution = const <TypeParameter, DartType>{};
|
||||
}
|
||||
|
||||
function.transformChildren(this);
|
||||
|
||||
Expression result = addClosure(function, contextVariable, parent.expression,
|
||||
typeSubstitution, enclosingTypeSubstitution);
|
||||
currentFunction = enclosingFunction;
|
||||
typeSubstitution = enclosingTypeSubstitution;
|
||||
return result;
|
||||
}
|
||||
|
||||
TreeNode visitFunctionDeclaration(FunctionDeclaration node) {
|
||||
/// Is this closure itself captured by a closure?
|
||||
bool isCaptured = capturedVariables.contains(node.variable);
|
||||
if (isCaptured) {
|
||||
context.extend(node.variable, new InvalidExpression());
|
||||
}
|
||||
Context parent = context;
|
||||
return saveContext(() {
|
||||
Expression expression = handleLocalFunction(node.function);
|
||||
|
||||
if (isCaptured) {
|
||||
parent.update(node.variable, expression);
|
||||
return null;
|
||||
} else {
|
||||
node.variable.initializer = expression;
|
||||
expression.parent = node.variable;
|
||||
return node.variable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TreeNode visitFunctionExpression(FunctionExpression node) => saveContext(() {
|
||||
return handleLocalFunction(node.function);
|
||||
});
|
||||
|
||||
/// Add a new class to the current library that looks like this:
|
||||
///
|
||||
/// class Closure#0 extends core::Object implements core::Function {
|
||||
/// field _in::Context context;
|
||||
/// constructor •(final _in::Context #t1) → dynamic
|
||||
/// : self::Closure 0::context = #t1
|
||||
/// ;
|
||||
/// method call(/* The parameters of [function] */) → dynamic {
|
||||
/// /// #t2 is [contextVariable].
|
||||
/// final _in::Context #t2 = this.{self::Closure#0::context};
|
||||
/// /* The body of [function]. */
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// Returns a constructor call to invoke the above constructor.
|
||||
///
|
||||
/// TODO(ahe): We shouldn't create a class for each closure. Instead we turn
|
||||
/// [function] into a top-level function and use the Dart VM's mechnism for
|
||||
/// closures.
|
||||
Expression addClosure(
|
||||
FunctionNode function,
|
||||
VariableDeclaration contextVariable,
|
||||
Expression accessContext,
|
||||
Map<TypeParameter, DartType> substitution,
|
||||
Map<TypeParameter, DartType> enclosingTypeSubstitution) {
|
||||
Field contextField = new Field(
|
||||
// TODO(ahe): Rename to #context.
|
||||
new Name("context"),
|
||||
type: contextClass.rawType,
|
||||
fileUri: currentFileUri);
|
||||
Class closureClass = createClosureClass(function,
|
||||
fields: [contextField], substitution: substitution);
|
||||
closureClass.addMember(new Procedure(
|
||||
new Name("call"), ProcedureKind.Method, function,
|
||||
fileUri: currentFileUri));
|
||||
newLibraryMembers.add(closureClass);
|
||||
Statement note = new ExpressionStatement(
|
||||
new StringLiteral("This is a temporary solution. "
|
||||
"In the VM, this will become an additional parameter."));
|
||||
List<Statement> statements = <Statement>[note, contextVariable];
|
||||
Statement body = function.body;
|
||||
if (body is Block) {
|
||||
statements.addAll(body.statements);
|
||||
} else {
|
||||
statements.add(body);
|
||||
}
|
||||
function.body = new Block(statements);
|
||||
function.body.parent = function;
|
||||
contextVariable.initializer =
|
||||
new PropertyGet(new ThisExpression(), contextField.name, contextField);
|
||||
contextVariable.initializer.parent = contextVariable;
|
||||
return new ConstructorInvocation(
|
||||
closureClass.constructors.single,
|
||||
new Arguments(<Expression>[accessContext], types:
|
||||
new List<DartType>.from(substitution.keys.map((TypeParameter t) {
|
||||
return substitute(
|
||||
new TypeParameterType(t), enclosingTypeSubstitution);
|
||||
}))));
|
||||
}
|
||||
|
||||
TreeNode visitField(Field node) {
|
||||
currentMember = node;
|
||||
context = new NoContext(this);
|
||||
if (node.isInstanceMember) {
|
||||
Name tearOffName = tearOffGetterNames[node.name];
|
||||
if (tearOffName != null) {
|
||||
// TODO(ahe): If we rewrite setters, we can rename the field to avoid
|
||||
// an indirection in most cases.
|
||||
addFieldForwarder(tearOffName, node);
|
||||
}
|
||||
}
|
||||
node = super.visitField(node);
|
||||
context = null;
|
||||
currentMember = null;
|
||||
return node;
|
||||
}
|
||||
|
||||
TreeNode visitProcedure(Procedure node) {
|
||||
currentMember = node;
|
||||
assert(_currentBlock == null);
|
||||
assert(_insertionIndex == 0);
|
||||
assert(context == null);
|
||||
|
||||
Statement body = node.function.body;
|
||||
|
||||
if (node.isInstanceMember) {
|
||||
Name tearOffName = tearOffGetterNames[node.name];
|
||||
if (tearOffName != null) {
|
||||
if (node.isGetter) {
|
||||
// We rename the getter to avoid an indirection in most cases.
|
||||
Name oldName = node.name;
|
||||
node.name = tearOffName;
|
||||
addGetterForwarder(oldName, node);
|
||||
} else if (node.kind == ProcedureKind.Method) {
|
||||
addTearOffGetter(tearOffName, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (body == null) return node;
|
||||
|
||||
currentMemberFunction = node.function;
|
||||
|
||||
// Ensure that the body is a block which becomes the current block.
|
||||
if (body is Block) {
|
||||
_currentBlock = body;
|
||||
} else {
|
||||
_currentBlock = new Block(<Statement>[body]);
|
||||
node.function.body = body.parent = _currentBlock;
|
||||
}
|
||||
_insertionIndex = 0;
|
||||
|
||||
// Start with no context. This happens after setting up _currentBlock
|
||||
// so statements can be emitted into _currentBlock if necessary.
|
||||
context = new NoContext(this);
|
||||
|
||||
VariableDeclaration self = thisAccess[currentMemberFunction];
|
||||
if (self != null) {
|
||||
context.extend(self, new ThisExpression());
|
||||
}
|
||||
|
||||
node.transformChildren(this);
|
||||
|
||||
_currentBlock = null;
|
||||
_insertionIndex = 0;
|
||||
context = null;
|
||||
currentMemberFunction = null;
|
||||
currentMember = null;
|
||||
return node;
|
||||
}
|
||||
|
||||
TreeNode visitLocalInitializer(LocalInitializer node) {
|
||||
assert(!capturedVariables.contains(node.variable));
|
||||
node.transformChildren(this);
|
||||
return node;
|
||||
}
|
||||
|
||||
TreeNode visitFunctionNode(FunctionNode node) {
|
||||
transformList(node.typeParameters, this, node);
|
||||
|
||||
void extend(VariableDeclaration parameter) {
|
||||
context.extend(parameter, new VariableGet(parameter));
|
||||
}
|
||||
|
||||
// TODO: Can parameters contain initializers (e.g., for optional ones) that
|
||||
// need to be closure converted?
|
||||
node.positionalParameters.where(capturedVariables.contains).forEach(extend);
|
||||
node.namedParameters.where(capturedVariables.contains).forEach(extend);
|
||||
|
||||
assert(node.body != null);
|
||||
node.body = node.body.accept(this);
|
||||
node.body.parent = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
TreeNode visitBlock(Block node) => saveContext(() {
|
||||
if (_currentBlock != node) {
|
||||
_currentBlock = node;
|
||||
_insertionIndex = 0;
|
||||
}
|
||||
|
||||
while (_insertionIndex < _currentBlock.statements.length) {
|
||||
assert(_currentBlock == node);
|
||||
|
||||
var original = _currentBlock.statements[_insertionIndex];
|
||||
var transformed = original.accept(this);
|
||||
assert(_currentBlock.statements[_insertionIndex] == original);
|
||||
if (transformed == null) {
|
||||
_currentBlock.statements.removeAt(_insertionIndex);
|
||||
} else {
|
||||
_currentBlock.statements[_insertionIndex++] = transformed;
|
||||
transformed.parent = _currentBlock;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
|
||||
TreeNode visitVariableDeclaration(VariableDeclaration node) {
|
||||
node.transformChildren(this);
|
||||
|
||||
if (!capturedVariables.contains(node)) return node;
|
||||
context.extend(node, node.initializer ?? new NullLiteral());
|
||||
|
||||
if (node.parent == currentFunction) return node;
|
||||
if (node.parent is Block) {
|
||||
// When returning null, the parent block will remove this node from its
|
||||
// list of statements.
|
||||
// TODO(ahe): I'd like to avoid testing on the parent pointer.
|
||||
return null;
|
||||
}
|
||||
throw "Unexpected parent for $node: ${node.parent.parent}";
|
||||
}
|
||||
|
||||
TreeNode visitVariableGet(VariableGet node) {
|
||||
return capturedVariables.contains(node.variable)
|
||||
? context.lookup(node.variable)
|
||||
: node;
|
||||
}
|
||||
|
||||
TreeNode visitVariableSet(VariableSet node) {
|
||||
node.transformChildren(this);
|
||||
|
||||
return capturedVariables.contains(node.variable)
|
||||
? context.assign(node.variable, node.value,
|
||||
voidContext: isInVoidContext(node))
|
||||
: node;
|
||||
}
|
||||
|
||||
bool isInVoidContext(Expression node) {
|
||||
TreeNode parent = node.parent;
|
||||
return parent is ExpressionStatement ||
|
||||
parent is ForStatement && parent.condition != node;
|
||||
}
|
||||
|
||||
DartType visitDartType(DartType node) {
|
||||
return substitute(node, typeSubstitution);
|
||||
}
|
||||
|
||||
VariableDeclaration getReplacementLoopVariable(VariableDeclaration variable) {
|
||||
VariableDeclaration newVariable = new VariableDeclaration(variable.name,
|
||||
initializer: variable.initializer,
|
||||
type: variable.type)..flags = variable.flags;
|
||||
variable.initializer = new VariableGet(newVariable);
|
||||
variable.initializer.parent = variable;
|
||||
return newVariable;
|
||||
}
|
||||
|
||||
Expression cloneContext() {
|
||||
InvalidExpression placeHolder = new InvalidExpression();
|
||||
contextClonePlaceHolders.add(placeHolder);
|
||||
return placeHolder;
|
||||
}
|
||||
|
||||
TreeNode visitInvalidExpression(InvalidExpression node) {
|
||||
return contextClonePlaceHolders.remove(node) ? context.clone() : node;
|
||||
}
|
||||
|
||||
TreeNode visitForStatement(ForStatement node) {
|
||||
if (node.variables.any(capturedVariables.contains)) {
|
||||
// In Dart, loop variables are new variables on each iteration of the
|
||||
// loop. This is only observable when a loop variable is captured by a
|
||||
// closure, which is the situation we're in here. So we transform the
|
||||
// loop.
|
||||
//
|
||||
// Consider the following example, where `x` is `node.variables.first`,
|
||||
// and `#t1` is a temporary variable:
|
||||
//
|
||||
// for (var x = 0; x < 10; x++) body;
|
||||
//
|
||||
// This is transformed to:
|
||||
//
|
||||
// {
|
||||
// var x = 0;
|
||||
// for (; x < 10; clone-context, x++) body;
|
||||
// }
|
||||
//
|
||||
// `clone-context` is a place-holder that will later be replaced by an
|
||||
// expression that clones the current closure context (see
|
||||
// [visitInvalidExpression]).
|
||||
return saveContext(() {
|
||||
context = context.toNestedContext();
|
||||
List<Statement> statements = <Statement>[];
|
||||
statements.addAll(node.variables);
|
||||
statements.add(node);
|
||||
node.variables.clear();
|
||||
node.updates.insert(0, cloneContext());
|
||||
_currentBlock = new Block(statements);
|
||||
_insertionIndex = 0;
|
||||
return _currentBlock.accept(this);
|
||||
});
|
||||
}
|
||||
return super.visitForStatement(node);
|
||||
}
|
||||
|
||||
TreeNode visitForInStatement(ForInStatement node) {
|
||||
if (capturedVariables.contains(node.variable)) {
|
||||
// In Dart, loop variables are new variables on each iteration of the
|
||||
// loop. This is only observable when the loop variable is captured by a
|
||||
// closure, so we need to transform the for-in loop when `node.variable`
|
||||
// is captured.
|
||||
//
|
||||
// Consider the following example, where `x` is `node.variable`, and
|
||||
// `#t1` is a temporary variable:
|
||||
//
|
||||
// for (var x in expr) body;
|
||||
//
|
||||
// Notice that we can assume that `x` doesn't have an initializer based
|
||||
// on invariants in the Kernel AST. This is transformed to:
|
||||
//
|
||||
// for (var #t1 in expr) { var x = #t1; body; }
|
||||
//
|
||||
// After this, we call super to apply the normal closure conversion to
|
||||
// the transformed for-in loop.
|
||||
VariableDeclaration variable = node.variable;
|
||||
VariableDeclaration newVariable = getReplacementLoopVariable(variable);
|
||||
node.variable = newVariable;
|
||||
newVariable.parent = node;
|
||||
node.body = new Block(<Statement>[variable, node.body]);
|
||||
node.body.parent = node;
|
||||
}
|
||||
return super.visitForInStatement(node);
|
||||
}
|
||||
|
||||
TreeNode visitThisExpression(ThisExpression node) {
|
||||
return isOuterMostContext
|
||||
? node
|
||||
: context.lookup(thisAccess[currentMemberFunction]);
|
||||
}
|
||||
|
||||
TreeNode visitStaticGet(StaticGet node) {
|
||||
Member target = node.target;
|
||||
if (target is Procedure && target.kind == ProcedureKind.Method) {
|
||||
Expression expression = getTearOffExpression(node.target);
|
||||
expression.transformChildren(this);
|
||||
return expression;
|
||||
}
|
||||
return super.visitStaticGet(node);
|
||||
}
|
||||
|
||||
TreeNode visitPropertyGet(PropertyGet node) {
|
||||
Name tearOffName = tearOffGetterNames[node.name];
|
||||
if (tearOffName != null) {
|
||||
node.name = tearOffName;
|
||||
}
|
||||
return super.visitPropertyGet(node);
|
||||
}
|
||||
|
||||
TreeNode visitCatch(Catch node) {
|
||||
VariableDeclaration exception = node.exception;
|
||||
VariableDeclaration stackTrace = node.stackTrace;
|
||||
if (stackTrace != null && capturedVariables.contains(stackTrace)) {
|
||||
Block block = node.body = ensureBlock(node.body);
|
||||
block.parent = node;
|
||||
node.stackTrace = new VariableDeclaration(null);
|
||||
node.stackTrace.parent = node;
|
||||
stackTrace.initializer = new VariableGet(node.stackTrace);
|
||||
block.statements.insert(0, stackTrace);
|
||||
stackTrace.parent = block;
|
||||
}
|
||||
if (exception != null && capturedVariables.contains(exception)) {
|
||||
Block block = node.body = ensureBlock(node.body);
|
||||
block.parent = node;
|
||||
node.exception = new VariableDeclaration(null);
|
||||
node.exception.parent = node;
|
||||
exception.initializer = new VariableGet(node.exception);
|
||||
block.statements.insert(0, exception);
|
||||
exception.parent = block;
|
||||
}
|
||||
return super.visitCatch(node);
|
||||
}
|
||||
|
||||
Block ensureBlock(Statement statement) {
|
||||
return statement is Block ? statement : new Block(<Statement>[statement]);
|
||||
}
|
||||
|
||||
/// Creates a closure that will invoke [procedure] and return an expression
|
||||
/// that instantiates that closure.
|
||||
Expression getTearOffExpression(Procedure procedure) {
|
||||
Map<TypeParameter, DartType> substitution = procedure.isInstanceMember
|
||||
// Note: we do not attempt to avoid copying type variables that aren't
|
||||
// used in the signature of [procedure]. It might be more economical to
|
||||
// only copy type variables that are used. However, we assume that
|
||||
// passing type arguments that match the enclosing class' type
|
||||
// variables will be handled most efficiently.
|
||||
? copyTypeVariables(procedure.enclosingClass.typeParameters)
|
||||
: const <TypeParameter, DartType>{};
|
||||
Expression receiver = null;
|
||||
List<Field> fields = null;
|
||||
if (procedure.isInstanceMember) {
|
||||
// TODO(ahe): Rename to #self.
|
||||
Field self = new Field(new Name("self"), fileUri: currentFileUri);
|
||||
self.type = substitute(procedure.enclosingClass.thisType, substitution);
|
||||
fields = <Field>[self];
|
||||
receiver = new PropertyGet(new ThisExpression(), self.name, self);
|
||||
}
|
||||
Class closureClass = createClosureClass(procedure.function,
|
||||
fields: fields, substitution: substitution);
|
||||
closureClass.addMember(new Procedure(new Name("call"), ProcedureKind.Method,
|
||||
forwardFunction(procedure, receiver, substitution),
|
||||
fileUri: currentFileUri));
|
||||
newLibraryMembers.add(closureClass);
|
||||
Arguments constructorArguments = procedure.isInstanceMember
|
||||
? new Arguments(<Expression>[new ThisExpression()])
|
||||
: new Arguments.empty();
|
||||
if (substitution.isNotEmpty) {
|
||||
constructorArguments.types
|
||||
.addAll(procedure.enclosingClass.thisType.typeArguments);
|
||||
}
|
||||
return new ConstructorInvocation(
|
||||
closureClass.constructors.single, constructorArguments);
|
||||
}
|
||||
|
||||
/// Creates a function that has the same signature as `procedure.function`
|
||||
/// and which forwards all arguments to `procedure`.
|
||||
FunctionNode forwardFunction(Procedure procedure, Expression receiver,
|
||||
Map<TypeParameter, DartType> substitution) {
|
||||
CloneVisitor cloner = substitution.isEmpty
|
||||
? this.cloner
|
||||
: new CloneWithoutBody(typeSubstitution: substitution);
|
||||
FunctionNode function = procedure.function;
|
||||
List<TypeParameter> typeParameters =
|
||||
function.typeParameters.map(cloner.clone).toList();
|
||||
List<VariableDeclaration> positionalParameters =
|
||||
function.positionalParameters.map(cloner.clone).toList();
|
||||
List<VariableDeclaration> namedParameters =
|
||||
function.namedParameters.map(cloner.clone).toList();
|
||||
// TODO(ahe): Clone or copy inferredReturnValue?
|
||||
InferredValue inferredReturnValue = null;
|
||||
|
||||
List<DartType> types = typeParameters
|
||||
.map((TypeParameter parameter) => new TypeParameterType(parameter))
|
||||
.toList();
|
||||
List<Expression> positional = positionalParameters
|
||||
.map((VariableDeclaration parameter) => new VariableGet(parameter))
|
||||
.toList();
|
||||
List<NamedExpression> named =
|
||||
namedParameters.map((VariableDeclaration parameter) {
|
||||
return new NamedExpression(parameter.name, new VariableGet(parameter));
|
||||
}).toList();
|
||||
|
||||
Arguments arguments = new Arguments(positional, types: types, named: named);
|
||||
InvocationExpression invocation = procedure.isInstanceMember
|
||||
? new MethodInvocation(receiver, procedure.name, arguments, procedure)
|
||||
: new StaticInvocation(procedure, arguments);
|
||||
return new FunctionNode(new ReturnStatement(invocation),
|
||||
typeParameters: typeParameters,
|
||||
positionalParameters: positionalParameters,
|
||||
namedParameters: namedParameters,
|
||||
requiredParameterCount: function.requiredParameterCount,
|
||||
returnType: substitute(function.returnType, substitution),
|
||||
inferredReturnValue: inferredReturnValue);
|
||||
}
|
||||
|
||||
/// Creates copies of the type variables in [original] and returns a
|
||||
/// substitution that can be passed to [substitute] to substitute all uses of
|
||||
/// [original] with their copies.
|
||||
Map<TypeParameter, DartType> copyTypeVariables(
|
||||
Iterable<TypeParameter> original) {
|
||||
if (original.isEmpty) return const <TypeParameter, DartType>{};
|
||||
Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
|
||||
for (TypeParameter t in original) {
|
||||
substitution[t] = new TypeParameterType(new TypeParameter(t.name));
|
||||
}
|
||||
substitution.forEach((TypeParameter t, DartType copy) {
|
||||
if (copy is TypeParameterType) {
|
||||
copy.parameter.bound = substitute(t.bound, substitution);
|
||||
}
|
||||
});
|
||||
return substitution;
|
||||
}
|
||||
|
||||
Class createClosureClass(FunctionNode function,
|
||||
{List<Field> fields, Map<TypeParameter, DartType> substitution}) {
|
||||
List<TypeParameter> typeParameters = new List<TypeParameter>.from(
|
||||
substitution.values
|
||||
.map((DartType t) => (t as TypeParameterType).parameter));
|
||||
Class closureClass = new Class(
|
||||
name: 'Closure#${localNames[function]}',
|
||||
supertype: new Supertype(coreTypes.objectClass, const <DartType>[]),
|
||||
typeParameters: typeParameters,
|
||||
implementedTypes: <Supertype>[
|
||||
new Supertype(coreTypes.functionClass, const <DartType>[])
|
||||
],
|
||||
fileUri: currentFileUri);
|
||||
addClosureClassNote(closureClass);
|
||||
|
||||
List<VariableDeclaration> parameters = <VariableDeclaration>[];
|
||||
List<Initializer> initializers = <Initializer>[];
|
||||
for (Field field in fields ?? const <Field>[]) {
|
||||
closureClass.addMember(field);
|
||||
VariableDeclaration parameter = new VariableDeclaration(field.name.name,
|
||||
type: field.type, isFinal: true);
|
||||
parameters.add(parameter);
|
||||
initializers.add(new FieldInitializer(field, new VariableGet(parameter)));
|
||||
}
|
||||
|
||||
closureClass.addMember(new Constructor(
|
||||
new FunctionNode(new EmptyStatement(),
|
||||
positionalParameters: parameters),
|
||||
name: new Name(""),
|
||||
initializers: initializers));
|
||||
|
||||
return closureClass;
|
||||
}
|
||||
|
||||
Statement forwardToThisProperty(Member node) {
|
||||
assert(node is Field || (node is Procedure && node.isGetter));
|
||||
return new ReturnStatement(
|
||||
new PropertyGet(new ThisExpression(), node.name, node));
|
||||
}
|
||||
|
||||
void addFieldForwarder(Name name, Field field) {
|
||||
newClassMembers.add(new Procedure(name, ProcedureKind.Getter,
|
||||
new FunctionNode(forwardToThisProperty(field)),
|
||||
fileUri: currentFileUri));
|
||||
}
|
||||
|
||||
Procedure copyWithBody(Procedure procedure, Statement body) {
|
||||
Procedure copy = cloner.clone(procedure);
|
||||
copy.function.body = body;
|
||||
copy.function.body.parent = copy.function;
|
||||
return copy;
|
||||
}
|
||||
|
||||
void addGetterForwarder(Name name, Procedure getter) {
|
||||
assert(getter.isGetter);
|
||||
newClassMembers
|
||||
.add(copyWithBody(getter, forwardToThisProperty(getter))..name = name);
|
||||
}
|
||||
|
||||
void addTearOffGetter(Name name, Procedure procedure) {
|
||||
newClassMembers.add(new Procedure(name, ProcedureKind.Getter,
|
||||
new FunctionNode(new ReturnStatement(getTearOffExpression(procedure))),
|
||||
fileUri: currentFileUri));
|
||||
}
|
||||
|
||||
// TODO(ahe): Remove this method when we don't generate closure classes
|
||||
// anymore.
|
||||
void addClosureClassNote(Class closureClass) {
|
||||
closureClass.addMember(new Field(new Name("note"),
|
||||
type: coreTypes.stringClass.rawType,
|
||||
initializer: new StringLiteral(
|
||||
"This is temporary. The VM doesn't need closure classes."),
|
||||
fileUri: currentFileUri));
|
||||
}
|
||||
}
|
206
pkg/kernel/lib/transformations/closure/info.dart
Normal file
206
pkg/kernel/lib/transformations/closure/info.dart
Normal file
|
@ -0,0 +1,206 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library kernel.transformations.closure.info;
|
||||
|
||||
import '../../ast.dart'
|
||||
show
|
||||
Class,
|
||||
Constructor,
|
||||
Field,
|
||||
FunctionDeclaration,
|
||||
FunctionNode,
|
||||
Member,
|
||||
Name,
|
||||
Procedure,
|
||||
ProcedureKind,
|
||||
PropertyGet,
|
||||
ThisExpression,
|
||||
TypeParameter,
|
||||
TypeParameterType,
|
||||
VariableDeclaration,
|
||||
VariableGet,
|
||||
VariableSet;
|
||||
|
||||
import '../../visitor.dart' show RecursiveVisitor;
|
||||
|
||||
class ClosureInfo extends RecursiveVisitor {
|
||||
FunctionNode currentFunction;
|
||||
final Map<VariableDeclaration, FunctionNode> function =
|
||||
<VariableDeclaration, FunctionNode>{};
|
||||
|
||||
final Set<VariableDeclaration> variables = new Set<VariableDeclaration>();
|
||||
|
||||
final Map<FunctionNode, Set<TypeParameter>> typeVariables =
|
||||
<FunctionNode, Set<TypeParameter>>{};
|
||||
|
||||
/// Map from members to synthetic variables for accessing `this` in a local
|
||||
/// function.
|
||||
final Map<FunctionNode, VariableDeclaration> thisAccess =
|
||||
<FunctionNode, VariableDeclaration>{};
|
||||
|
||||
final Set<String> currentMemberLocalNames = new Set<String>();
|
||||
|
||||
final Map<FunctionNode, String> localNames = <FunctionNode, String>{};
|
||||
|
||||
/// Contains all names used as getter through a [PropertyGet].
|
||||
final Set<Name> invokedGetters = new Set<Name>();
|
||||
|
||||
/// Contains all names of declared regular instance methods (not including
|
||||
/// accessors and operators).
|
||||
final Set<Name> declaredInstanceMethodNames = new Set<Name>();
|
||||
|
||||
Class currentClass;
|
||||
|
||||
Member currentMember;
|
||||
|
||||
FunctionNode currentMemberFunction;
|
||||
|
||||
bool get isOuterMostContext {
|
||||
return currentFunction == null || currentMemberFunction == currentFunction;
|
||||
}
|
||||
|
||||
/// Maps the names of all instance methods that may be torn off (aka
|
||||
/// implicitly closurized) to `${name.name}#get`.
|
||||
Map<Name, Name> get tearOffGetterNames {
|
||||
Map<Name, Name> result = <Name, Name>{};
|
||||
for (Name name in declaredInstanceMethodNames) {
|
||||
if (invokedGetters.contains(name)) {
|
||||
result[name] = new Name("${name.name}#get", name.library);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void beginMember(Member member, [FunctionNode function]) {
|
||||
currentMemberLocalNames.clear();
|
||||
if (function != null) {
|
||||
localNames[function] = computeUniqueLocalName(member.name.name);
|
||||
}
|
||||
currentMember = member;
|
||||
currentMemberFunction = function;
|
||||
}
|
||||
|
||||
void endMember() {
|
||||
currentMember = null;
|
||||
currentMemberFunction = null;
|
||||
}
|
||||
|
||||
visitClass(Class node) {
|
||||
currentClass = node;
|
||||
super.visitClass(node);
|
||||
currentClass = null;
|
||||
}
|
||||
|
||||
visitConstructor(Constructor node) {
|
||||
beginMember(node, node.function);
|
||||
super.visitConstructor(node);
|
||||
endMember();
|
||||
}
|
||||
|
||||
visitProcedure(Procedure node) {
|
||||
beginMember(node, node.function);
|
||||
if (node.isInstanceMember && node.kind == ProcedureKind.Method) {
|
||||
// Ignore the `length` method of [File] subclasses for now, as they
|
||||
// will force us to rename the `length` getter (kernel issue #43).
|
||||
// TODO(ahe): remove this condition.
|
||||
Class parent = node.parent;
|
||||
if (node.name.name != "length" ||
|
||||
parent.enclosingLibrary.importUri.toString() != "dart:io") {
|
||||
declaredInstanceMethodNames.add(node.name);
|
||||
}
|
||||
}
|
||||
super.visitProcedure(node);
|
||||
endMember();
|
||||
}
|
||||
|
||||
visitField(Field node) {
|
||||
beginMember(node);
|
||||
super.visitField(node);
|
||||
endMember();
|
||||
}
|
||||
|
||||
String computeUniqueLocalName([String name]) {
|
||||
if (name == null || name.isEmpty) {
|
||||
name = "function";
|
||||
}
|
||||
if (currentFunction == null) {
|
||||
if (currentMember != null) {
|
||||
name = "${currentMember.name.name}#$name";
|
||||
}
|
||||
if (currentClass != null) {
|
||||
name = "${currentClass.name}#$name";
|
||||
}
|
||||
} else {
|
||||
name = "${localNames[currentFunction]}#$name";
|
||||
}
|
||||
int count = 1;
|
||||
String candidate = name;
|
||||
while (currentMemberLocalNames.contains(candidate)) {
|
||||
candidate = "$name#${count++}";
|
||||
}
|
||||
currentMemberLocalNames.add(candidate);
|
||||
return candidate;
|
||||
}
|
||||
|
||||
visitFunctionDeclaration(FunctionDeclaration node) {
|
||||
assert(!localNames.containsKey(node));
|
||||
localNames[node.function] = computeUniqueLocalName(node.variable.name);
|
||||
return super.visitFunctionDeclaration(node);
|
||||
}
|
||||
|
||||
visitFunctionNode(FunctionNode node) {
|
||||
localNames.putIfAbsent(node, computeUniqueLocalName);
|
||||
var saved = currentFunction;
|
||||
currentFunction = node;
|
||||
node.visitChildren(this);
|
||||
currentFunction = saved;
|
||||
Set<TypeParameter> capturedTypeVariables = typeVariables[node];
|
||||
if (capturedTypeVariables != null && !isOuterMostContext) {
|
||||
// Propagate captured type variables to enclosing function.
|
||||
typeVariables
|
||||
.putIfAbsent(currentFunction, () => new Set<TypeParameter>())
|
||||
.addAll(capturedTypeVariables);
|
||||
}
|
||||
}
|
||||
|
||||
visitVariableDeclaration(VariableDeclaration node) {
|
||||
function[node] = currentFunction;
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
visitVariableGet(VariableGet node) {
|
||||
if (function[node.variable] != currentFunction) {
|
||||
variables.add(node.variable);
|
||||
}
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
visitVariableSet(VariableSet node) {
|
||||
if (function[node.variable] != currentFunction) {
|
||||
variables.add(node.variable);
|
||||
}
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
visitTypeParameterType(TypeParameterType node) {
|
||||
if (!isOuterMostContext) {
|
||||
typeVariables
|
||||
.putIfAbsent(currentFunction, () => new Set<TypeParameter>())
|
||||
.add(node.parameter);
|
||||
}
|
||||
}
|
||||
|
||||
visitThisExpression(ThisExpression node) {
|
||||
if (!isOuterMostContext) {
|
||||
thisAccess.putIfAbsent(
|
||||
currentMemberFunction, () => new VariableDeclaration("#self"));
|
||||
}
|
||||
}
|
||||
|
||||
visitPropertyGet(PropertyGet node) {
|
||||
invokedGetters.add(node.name);
|
||||
super.visitPropertyGet(node);
|
||||
}
|
||||
}
|
188
pkg/kernel/lib/transformations/closure/mock.dart
Normal file
188
pkg/kernel/lib/transformations/closure/mock.dart
Normal file
|
@ -0,0 +1,188 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library kernel.transformations.closure.mock;
|
||||
|
||||
import '../../ast.dart'
|
||||
show
|
||||
Arguments,
|
||||
Block,
|
||||
Class,
|
||||
Constructor,
|
||||
ConstructorInvocation,
|
||||
DartType,
|
||||
DynamicType,
|
||||
EmptyStatement,
|
||||
Expression,
|
||||
ExpressionStatement,
|
||||
Field,
|
||||
FieldInitializer,
|
||||
FunctionNode,
|
||||
Initializer,
|
||||
IntLiteral,
|
||||
Library,
|
||||
MethodInvocation,
|
||||
Name,
|
||||
Procedure,
|
||||
ProcedureKind,
|
||||
Program,
|
||||
PropertyGet,
|
||||
ReturnStatement,
|
||||
Statement,
|
||||
StaticInvocation,
|
||||
Supertype,
|
||||
VariableDeclaration,
|
||||
VariableGet;
|
||||
|
||||
import '../../core_types.dart' show CoreTypes;
|
||||
|
||||
import '../../frontend/accessors.dart'
|
||||
show
|
||||
Accessor,
|
||||
IndexAccessor,
|
||||
PropertyAccessor,
|
||||
ThisPropertyAccessor,
|
||||
VariableAccessor;
|
||||
|
||||
/// Extend the program with this mock:
|
||||
///
|
||||
/// class Context {
|
||||
/// final List list;
|
||||
/// var parent;
|
||||
/// Context(int i) : list = new List(i);
|
||||
/// operator[] (int i) => list[i];
|
||||
/// operator[]= (int i, value) {
|
||||
/// list[i] = value;
|
||||
/// }
|
||||
/// Context copy() {
|
||||
/// Context c = new Context(list.length);
|
||||
/// c.parent = parent;
|
||||
/// c.list.setRange(0, list.length, list);
|
||||
/// return c;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// Returns the mock.
|
||||
Class mockUpContext(CoreTypes coreTypes, Program program) {
|
||||
String fileUri = "dart:mock";
|
||||
|
||||
/// final List list;
|
||||
Field listField = new Field(new Name("list"),
|
||||
type: coreTypes.listClass.rawType, isFinal: true, fileUri: fileUri);
|
||||
Accessor listFieldAccessor =
|
||||
new ThisPropertyAccessor(listField.name, listField, null);
|
||||
|
||||
/// var parent;
|
||||
Field parentField = new Field(new Name("parent"), fileUri: fileUri);
|
||||
Accessor parentFieldAccessor =
|
||||
new ThisPropertyAccessor(parentField.name, parentField, parentField);
|
||||
|
||||
List<Field> fields = <Field>[listField, parentField];
|
||||
|
||||
/// Context(int i) : list = new List(i);
|
||||
VariableDeclaration iParameter = new VariableDeclaration("i",
|
||||
type: coreTypes.intClass.rawType, isFinal: true);
|
||||
Constructor constructor = new Constructor(
|
||||
new FunctionNode(new EmptyStatement(),
|
||||
positionalParameters: <VariableDeclaration>[iParameter]),
|
||||
name: new Name(""),
|
||||
initializers: <Initializer>[
|
||||
new FieldInitializer(
|
||||
listField,
|
||||
new StaticInvocation(
|
||||
coreTypes.listClass.procedures.first,
|
||||
new Arguments(<Expression>[
|
||||
new VariableAccessor(iParameter).buildSimpleRead()
|
||||
], types: <DartType>[
|
||||
const DynamicType()
|
||||
])))
|
||||
]);
|
||||
|
||||
/// operator[] (int i) => list[i];
|
||||
iParameter = new VariableDeclaration("i",
|
||||
type: coreTypes.intClass.rawType, isFinal: true);
|
||||
Accessor accessor = IndexAccessor.make(listFieldAccessor.buildSimpleRead(),
|
||||
new VariableAccessor(iParameter).buildSimpleRead(), null, null);
|
||||
Procedure indexGet = new Procedure(
|
||||
new Name("[]"),
|
||||
ProcedureKind.Operator,
|
||||
new FunctionNode(new ReturnStatement(accessor.buildSimpleRead()),
|
||||
positionalParameters: <VariableDeclaration>[iParameter]),
|
||||
fileUri: fileUri);
|
||||
|
||||
/// operator[]= (int i, value) {
|
||||
/// list[i] = value;
|
||||
/// }
|
||||
iParameter = new VariableDeclaration("i",
|
||||
type: coreTypes.intClass.rawType, isFinal: true);
|
||||
VariableDeclaration valueParameter =
|
||||
new VariableDeclaration("value", isFinal: true);
|
||||
accessor = IndexAccessor.make(listFieldAccessor.buildSimpleRead(),
|
||||
new VariableAccessor(iParameter).buildSimpleRead(), null, null);
|
||||
Expression expression = accessor.buildAssignment(
|
||||
new VariableAccessor(valueParameter).buildSimpleRead(),
|
||||
voidContext: true);
|
||||
Procedure indexSet = new Procedure(
|
||||
new Name("[]="),
|
||||
ProcedureKind.Operator,
|
||||
new FunctionNode(new ExpressionStatement(expression),
|
||||
positionalParameters: <VariableDeclaration>[
|
||||
iParameter,
|
||||
valueParameter
|
||||
]),
|
||||
fileUri: fileUri);
|
||||
|
||||
/// Context copy() {
|
||||
/// Context c = new Context(list.length);
|
||||
/// c.parent = parent;
|
||||
/// c.list.setRange(0, list.length, list);
|
||||
/// return c;
|
||||
/// }
|
||||
VariableDeclaration c = new VariableDeclaration("c",
|
||||
initializer: new ConstructorInvocation(
|
||||
constructor,
|
||||
new Arguments(<Expression>[
|
||||
new PropertyGet(
|
||||
listFieldAccessor.buildSimpleRead(), new Name("length"))
|
||||
])));
|
||||
Accessor accessCParent = PropertyAccessor.make(
|
||||
new VariableGet(c), parentField.name, parentField, parentField);
|
||||
Accessor accessCList = PropertyAccessor.make(
|
||||
new VariableGet(c), listField.name, listField, null);
|
||||
List<Statement> statements = <Statement>[
|
||||
c,
|
||||
new ExpressionStatement(accessCParent.buildAssignment(
|
||||
parentFieldAccessor.buildSimpleRead(),
|
||||
voidContext: true)),
|
||||
new ExpressionStatement(new MethodInvocation(
|
||||
accessCList.buildSimpleRead(),
|
||||
new Name("setRange"),
|
||||
new Arguments(<Expression>[
|
||||
new IntLiteral(0),
|
||||
new PropertyGet(
|
||||
listFieldAccessor.buildSimpleRead(), new Name("length")),
|
||||
listFieldAccessor.buildSimpleRead()
|
||||
]))),
|
||||
new ReturnStatement(new VariableGet(c))
|
||||
];
|
||||
Procedure copy = new Procedure(new Name("copy"), ProcedureKind.Method,
|
||||
new FunctionNode(new Block(statements)),
|
||||
fileUri: fileUri);
|
||||
|
||||
List<Procedure> procedures = <Procedure>[indexGet, indexSet, copy];
|
||||
|
||||
Class contextClass = new Class(
|
||||
name: "Context",
|
||||
supertype: new Supertype(coreTypes.objectClass, const <DartType>[]),
|
||||
constructors: [constructor],
|
||||
fields: fields,
|
||||
procedures: procedures,
|
||||
fileUri: fileUri);
|
||||
Library mock = new Library(Uri.parse(fileUri),
|
||||
name: "mock", classes: [contextClass])..fileUri = fileUri;
|
||||
program.libraries.add(mock);
|
||||
mock.parent = program;
|
||||
program.uriToLineStarts[mock.fileUri] = <int>[0];
|
||||
return contextClass;
|
||||
}
|
25
pkg/kernel/lib/transformations/closure_conversion.dart
Normal file
25
pkg/kernel/lib/transformations/closure_conversion.dart
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library kernel.transformations.closure_conversion;
|
||||
|
||||
import '../ast.dart' show Class, Program;
|
||||
|
||||
import '../core_types.dart' show CoreTypes;
|
||||
|
||||
import 'closure/converter.dart' show ClosureConverter;
|
||||
|
||||
import 'closure/info.dart' show ClosureInfo;
|
||||
|
||||
import 'closure/mock.dart' show mockUpContext;
|
||||
|
||||
Program transformProgram(Program program) {
|
||||
var info = new ClosureInfo();
|
||||
info.visitProgram(program);
|
||||
|
||||
CoreTypes coreTypes = new CoreTypes(program);
|
||||
Class contextClass = mockUpContext(coreTypes, program);
|
||||
var convert = new ClosureConverter(coreTypes, info, contextClass);
|
||||
return convert.visitProgram(program);
|
||||
}
|
|
@ -99,8 +99,8 @@ class Uint31PairMap<T> {
|
|||
Iterable<T> get values => _table.values;
|
||||
|
||||
static int _bigintHash(int bigint) {
|
||||
int x = 0x3fffffff & (bigint >> 31);
|
||||
int y = 0x3fffffff & bigint;
|
||||
int x = 0x3fffffff & (bigint >> 31);
|
||||
int y = 0x3fffffff & bigint;
|
||||
int hash = 0x3fffffff & (x * 1367);
|
||||
hash = 0x3fffffff & (y * 31 + hash ^ y);
|
||||
hash = 0x3fffffff & ((x ^ y) * 31 + hash ^ y);
|
||||
|
|
|
@ -51,7 +51,12 @@ class TypePropagation {
|
|||
}
|
||||
}
|
||||
|
||||
enum BaseClassKind { None, Exact, Subclass, Subtype, }
|
||||
enum BaseClassKind {
|
||||
None,
|
||||
Exact,
|
||||
Subclass,
|
||||
Subtype,
|
||||
}
|
||||
|
||||
/// An abstract value inferred by type propagation.
|
||||
///
|
||||
|
|
|
@ -235,9 +235,7 @@ class MemberReferenceVisitor<R> {
|
|||
}
|
||||
|
||||
class Visitor<R> extends TreeVisitor<R>
|
||||
implements
|
||||
DartTypeVisitor<R>,
|
||||
MemberReferenceVisitor<R> {
|
||||
implements DartTypeVisitor<R>, MemberReferenceVisitor<R> {
|
||||
/// The catch-all case, except for references.
|
||||
R defaultNode(Node node) => null;
|
||||
R defaultTreeNode(TreeNode node) => defaultNode(node);
|
||||
|
|
|
@ -6,7 +6,7 @@ homepage: https://github.com/dart-lang/kernel
|
|||
environment:
|
||||
sdk: ">=1.8.0"
|
||||
dependencies:
|
||||
analyzer: ^0.29.0
|
||||
analyzer: ^0.30.0-alpha.0
|
||||
path: ^1.3.9
|
||||
args: ^0.13.4
|
||||
logging: ^0.11.2
|
||||
|
@ -15,5 +15,9 @@ dev_dependencies:
|
|||
test: ^0.12.15+6
|
||||
stack_trace: ^1.6.6
|
||||
ansicolor: ^0.0.9
|
||||
testing:
|
||||
git:
|
||||
url: https://github.com/peter-ahe-google/testing.git
|
||||
ref: 2e196d5
|
||||
dependency_overrides:
|
||||
analyzer: {path: ../analyzer}
|
||||
|
|
|
@ -43,6 +43,7 @@ class BasicClassHierarchy {
|
|||
callback(member, superMember, setter);
|
||||
}
|
||||
}
|
||||
|
||||
// Report declared members overriding inheritable members.
|
||||
for (var member in class_.mixin.members) {
|
||||
for (var supertype in class_.supers) {
|
||||
|
@ -172,6 +173,7 @@ class BasicClassHierarchy {
|
|||
interfaceGettersAndCalls[node]);
|
||||
mergeMaps(interfaceSetters[type.classNode], interfaceSetters[node]);
|
||||
}
|
||||
|
||||
inheritFrom(node.supertype);
|
||||
inheritFrom(node.mixedInType);
|
||||
node.implementedTypes.forEach(inheritFrom);
|
||||
|
|
|
@ -10,7 +10,8 @@ import 'dart:io';
|
|||
|
||||
ArgParser argParser = new ArgParser()
|
||||
..addFlag('basic', help: 'Measure the basic implementation', negatable: false)
|
||||
..addOption('count', abbr: 'c',
|
||||
..addOption('count',
|
||||
abbr: 'c',
|
||||
help: 'Build N copies of the class hierarchy',
|
||||
defaultsTo: '300');
|
||||
|
||||
|
|
|
@ -115,12 +115,14 @@ void testClassHierarchyOnProgram(Program program, {bool verbose: false}) {
|
|||
String eq = setter ? '=' : '';
|
||||
return '$member$eq overrides $superMember$eq';
|
||||
}
|
||||
|
||||
Set<String> expectedOverrides = new Set<String>();
|
||||
basic.forEachOverridePair(classNode, (member, superMember, setter) {
|
||||
expectedOverrides.add(getHash(member, superMember, setter));
|
||||
});
|
||||
Set<String> actualOverrides = new Set<String>();
|
||||
classHierarchy.forEachOverridePair(classNode, (member, superMember, setter) {
|
||||
classHierarchy.forEachOverridePair(classNode,
|
||||
(member, superMember, setter) {
|
||||
actualOverrides.add(getHash(member, superMember, setter));
|
||||
});
|
||||
for (var actual in actualOverrides) {
|
||||
|
@ -142,7 +144,7 @@ void testClassHierarchyOnProgram(Program program, {bool verbose: false}) {
|
|||
var random = new Random(12345);
|
||||
|
||||
List/*<T>*/ pickRandom/*<T>*/(List/*<T>*/ items, int n) {
|
||||
var result = /*<T>*/[];
|
||||
var result = /*<T>*/ [];
|
||||
for (int i = 0; i < n; ++i) {
|
||||
result.add(items[random.nextInt(items.length)]);
|
||||
}
|
||||
|
|
199
pkg/kernel/test/closures/kernel_chain.dart
Normal file
199
pkg/kernel/test/closures/kernel_chain.dart
Normal file
|
@ -0,0 +1,199 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
library test.kernel.closures.suite;
|
||||
|
||||
import 'dart:async' show Future;
|
||||
|
||||
import 'dart:io' show Directory, File, IOSink;
|
||||
|
||||
import 'dart:typed_data' show Uint8List;
|
||||
|
||||
import 'package:kernel/kernel.dart' show Repository, loadProgramFromBinary;
|
||||
|
||||
import 'package:kernel/text/ast_to_text.dart' show Printer;
|
||||
|
||||
import 'package:testing/testing.dart'
|
||||
show ChainContext, Result, StdioProcess, Step;
|
||||
|
||||
import 'package:kernel/ast.dart' show Library, Program;
|
||||
|
||||
import 'package:kernel/verifier.dart' show verifyProgram;
|
||||
|
||||
import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
|
||||
|
||||
import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
|
||||
|
||||
import 'package:kernel/binary/loader.dart' show BinaryLoader;
|
||||
|
||||
Future<bool> fileExists(Uri base, String path) async {
|
||||
return await new File.fromUri(base.resolve(path)).exists();
|
||||
}
|
||||
|
||||
class Print extends Step<Program, Program, ChainContext> {
|
||||
const Print();
|
||||
|
||||
String get name => "print";
|
||||
|
||||
Future<Result<Program>> run(Program program, _) async {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (Library library in program.libraries) {
|
||||
Printer printer = new Printer(sb);
|
||||
if (library.importUri.scheme != "dart") {
|
||||
printer.writeLibraryFile(library);
|
||||
}
|
||||
}
|
||||
print("$sb");
|
||||
return pass(program);
|
||||
}
|
||||
}
|
||||
|
||||
class SanityCheck extends Step<Program, Program, ChainContext> {
|
||||
const SanityCheck();
|
||||
|
||||
String get name => "sanity check";
|
||||
|
||||
Future<Result<Program>> run(Program program, _) async {
|
||||
try {
|
||||
verifyProgram(program);
|
||||
return pass(program);
|
||||
} catch (e, s) {
|
||||
return crash(e, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MatchExpectation extends Step<Program, Program, ChainContext> {
|
||||
final String suffix;
|
||||
|
||||
final bool updateExpectations;
|
||||
|
||||
const MatchExpectation(this.suffix, {this.updateExpectations: true});
|
||||
|
||||
String get name => "match expectations";
|
||||
|
||||
Future<Result<Program>> run(Program program, _) async {
|
||||
Library library = program.libraries
|
||||
.firstWhere((Library library) => library.importUri.scheme != "dart");
|
||||
Uri uri = library.importUri;
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
new Printer(buffer).writeLibraryFile(library);
|
||||
|
||||
File expectedFile = new File("${uri.toFilePath()}$suffix");
|
||||
if (await expectedFile.exists()) {
|
||||
String expected = await expectedFile.readAsString();
|
||||
if (expected.trim() != "$buffer".trim()) {
|
||||
if (!updateExpectations) {
|
||||
String diff = await runDiff(expectedFile.uri, "$buffer");
|
||||
return fail(null, "$uri doesn't match ${expectedFile.uri}\n$diff");
|
||||
}
|
||||
} else {
|
||||
return pass(program);
|
||||
}
|
||||
}
|
||||
if (updateExpectations) {
|
||||
await openWrite(expectedFile.uri, (IOSink sink) {
|
||||
sink.writeln("$buffer".trim());
|
||||
});
|
||||
return pass(program);
|
||||
} else {
|
||||
return fail(
|
||||
program,
|
||||
"""
|
||||
Please create file ${expectedFile.path} with this content:
|
||||
$buffer""");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WriteDill extends Step<Program, Uri, ChainContext> {
|
||||
const WriteDill();
|
||||
|
||||
String get name => "write .dill";
|
||||
|
||||
Future<Result<Uri>> run(Program program, _) async {
|
||||
Directory tmp = await Directory.systemTemp.createTemp();
|
||||
Uri uri = tmp.uri.resolve("generated.dill");
|
||||
File generated = new File.fromUri(uri);
|
||||
IOSink sink = generated.openWrite();
|
||||
try {
|
||||
new BinaryPrinter(sink).writeProgramFile(program);
|
||||
} catch (e, s) {
|
||||
return fail(uri, e, s);
|
||||
} finally {
|
||||
print("Wrote `${generated.path}`");
|
||||
await sink.close();
|
||||
}
|
||||
return pass(uri);
|
||||
}
|
||||
}
|
||||
|
||||
class ReadDill extends Step<Uri, Uri, ChainContext> {
|
||||
const ReadDill();
|
||||
|
||||
String get name => "read .dill";
|
||||
|
||||
Future<Result<Uri>> run(Uri uri, _) async {
|
||||
try {
|
||||
loadProgramFromBinary(uri.toFilePath());
|
||||
} catch (e, s) {
|
||||
return fail(uri, e, s);
|
||||
}
|
||||
return pass(uri);
|
||||
}
|
||||
}
|
||||
|
||||
class Copy extends Step<Program, Program, ChainContext> {
|
||||
const Copy();
|
||||
|
||||
String get name => "copy program";
|
||||
|
||||
Future<Result<Program>> run(Program program, _) async {
|
||||
BytesCollector sink = new BytesCollector();
|
||||
new BinaryPrinter(sink).writeProgramFile(program);
|
||||
Uint8List bytes = sink.collect();
|
||||
BinaryLoader loader = new BinaryLoader(new Repository());
|
||||
return pass(new BinaryBuilder(loader, bytes).readProgramFile());
|
||||
}
|
||||
}
|
||||
|
||||
class BytesCollector implements Sink<List<int>> {
|
||||
final List<List<int>> lists = <List<int>>[];
|
||||
|
||||
int length = 0;
|
||||
|
||||
void add(List<int> data) {
|
||||
lists.add(data);
|
||||
length += data.length;
|
||||
}
|
||||
|
||||
Uint8List collect() {
|
||||
Uint8List result = new Uint8List(length);
|
||||
int offset = 0;
|
||||
for (List<int> list in lists) {
|
||||
result.setRange(offset, offset += list.length, list);
|
||||
}
|
||||
lists.clear();
|
||||
length = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void close() {}
|
||||
}
|
||||
|
||||
Future<String> runDiff(Uri expected, String actual) async {
|
||||
StdioProcess process = await StdioProcess
|
||||
.run("diff", <String>["-u", expected.toFilePath(), "-"], input: actual);
|
||||
return process.output;
|
||||
}
|
||||
|
||||
Future openWrite(Uri uri, f(IOSink sink)) async {
|
||||
IOSink sink = new File.fromUri(uri).openWrite();
|
||||
try {
|
||||
await f(sink);
|
||||
} finally {
|
||||
await sink.close();
|
||||
}
|
||||
print("Wrote $uri");
|
||||
}
|
193
pkg/kernel/test/closures/suite.dart
Normal file
193
pkg/kernel/test/closures/suite.dart
Normal file
|
@ -0,0 +1,193 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
library test.kernel.closures.suite;
|
||||
|
||||
import 'dart:async' show Future;
|
||||
|
||||
import 'dart:io' show Directory, File, Platform;
|
||||
|
||||
import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
|
||||
|
||||
import 'package:kernel/analyzer/loader.dart'
|
||||
show DartLoader, DartOptions, createDartSdk;
|
||||
|
||||
import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
|
||||
|
||||
import 'package:kernel/repository.dart' show Repository;
|
||||
|
||||
import 'kernel_chain.dart'
|
||||
show MatchExpectation, Print, ReadDill, SanityCheck, WriteDill;
|
||||
|
||||
import 'package:testing/testing.dart'
|
||||
show
|
||||
Chain,
|
||||
ChainContext,
|
||||
Result,
|
||||
StdioProcess,
|
||||
Step,
|
||||
TestDescription,
|
||||
runMe;
|
||||
|
||||
import 'package:kernel/ast.dart' show Program;
|
||||
|
||||
import 'package:kernel/transformations/closure_conversion.dart'
|
||||
as closure_conversion;
|
||||
|
||||
import 'package:package_config/discovery.dart' show loadPackagesFile;
|
||||
|
||||
class TestContext extends ChainContext {
|
||||
final Uri vm;
|
||||
|
||||
final Uri packages;
|
||||
|
||||
final DartOptions options;
|
||||
|
||||
final DartSdk dartSdk;
|
||||
|
||||
final List<Step> steps;
|
||||
|
||||
TestContext(String sdk, this.vm, Uri packages, bool strongMode, this.dartSdk,
|
||||
bool updateExpectations)
|
||||
: packages = packages,
|
||||
options = new DartOptions(
|
||||
strongMode: strongMode,
|
||||
sdk: sdk,
|
||||
packagePath: packages.toFilePath()),
|
||||
steps = <Step>[
|
||||
const Kernel(),
|
||||
const Print(),
|
||||
const SanityCheck(),
|
||||
const ClosureConversion(),
|
||||
const Print(),
|
||||
const SanityCheck(),
|
||||
new MatchExpectation(".expect",
|
||||
updateExpectations: updateExpectations),
|
||||
const WriteDill(),
|
||||
const ReadDill(),
|
||||
const Run(),
|
||||
];
|
||||
|
||||
Future<DartLoader> createLoader() async {
|
||||
Repository repository = new Repository();
|
||||
return new DartLoader(repository, options, await loadPackagesFile(packages),
|
||||
dartSdk: dartSdk);
|
||||
}
|
||||
}
|
||||
|
||||
enum Environment {
|
||||
directory,
|
||||
file,
|
||||
}
|
||||
|
||||
Future<String> getEnvironmentVariable(
|
||||
String name, Environment kind, String undefined, notFound(String n)) async {
|
||||
String result = Platform.environment[name];
|
||||
if (result == null) {
|
||||
throw undefined;
|
||||
}
|
||||
switch (kind) {
|
||||
case Environment.directory:
|
||||
if (!await new Directory(result).exists()) throw notFound(result);
|
||||
break;
|
||||
|
||||
case Environment.file:
|
||||
if (!await new File(result).exists()) throw notFound(result);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<bool> fileExists(Uri base, String path) async {
|
||||
return await new File.fromUri(base.resolve(path)).exists();
|
||||
}
|
||||
|
||||
Future<TestContext> createContext(
|
||||
Chain suite, Map<String, String> environment) async {
|
||||
const String suggestion = """Try building the patched SDK by running
|
||||
'tools/build.py patched_sdk'""";
|
||||
|
||||
// TODO(karlklose): The path is different on MacOS.
|
||||
String sdk = "out/DebugX64/patched_sdk/";
|
||||
Uri sdkUri = Uri.base.resolve(sdk);
|
||||
const String asyncDart = "lib/async/async.dart";
|
||||
if (!await fileExists(sdkUri, asyncDart)) {
|
||||
throw "Couldn't find the patched SDK. $suggestion";
|
||||
}
|
||||
const String asyncSources = "lib/async/async_sources.gypi";
|
||||
if (await fileExists(sdkUri, asyncSources)) {
|
||||
throw "Found '$asyncSources' in '$sdk', so it isn't a patched SDK. "
|
||||
"$suggestion";
|
||||
}
|
||||
|
||||
// TODO(karlklose): select the VM based on the mode.
|
||||
Uri vm = Uri.base.resolve("out/ReleaseX64/dart");
|
||||
|
||||
Uri packages = Uri.base.resolve(".packages");
|
||||
bool strongMode = false;
|
||||
bool updateExpectations = environment["updateExpectations"] != "false";
|
||||
return new TestContext(sdk, vm, packages, strongMode,
|
||||
createDartSdk(sdk, strongMode: strongMode), updateExpectations);
|
||||
}
|
||||
|
||||
class Kernel extends Step<TestDescription, Program, TestContext> {
|
||||
const Kernel();
|
||||
|
||||
String get name => "kernel";
|
||||
|
||||
Future<Result<Program>> run(
|
||||
TestDescription description, TestContext testContext) async {
|
||||
try {
|
||||
DartLoader loader = await testContext.createLoader();
|
||||
Target target = getTarget(
|
||||
"vm", new TargetFlags(strongMode: testContext.options.strongMode));
|
||||
String path = description.file.path;
|
||||
Uri uri = Uri.base.resolve(path);
|
||||
Program program = loader.loadProgram(uri, target: target);
|
||||
for (var error in loader.errors) {
|
||||
return fail(program, "$error");
|
||||
}
|
||||
target.transformProgram(program);
|
||||
return pass(program);
|
||||
} catch (e, s) {
|
||||
return crash(e, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ClosureConversion extends Step<Program, Program, TestContext> {
|
||||
const ClosureConversion();
|
||||
|
||||
String get name => "closure conversion";
|
||||
|
||||
Future<Result<Program>> run(Program program, TestContext testContext) async {
|
||||
try {
|
||||
program = closure_conversion.transformProgram(program);
|
||||
return pass(program);
|
||||
} catch (e, s) {
|
||||
return crash(e, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Run extends Step<Uri, int, TestContext> {
|
||||
const Run();
|
||||
|
||||
String get name => "run";
|
||||
|
||||
Future<Result<int>> run(Uri uri, TestContext context) async {
|
||||
File generated = new File.fromUri(uri);
|
||||
StdioProcess process;
|
||||
try {
|
||||
process = await StdioProcess
|
||||
.run(context.vm.toFilePath(), [generated.path, "Hello, World!"]);
|
||||
print(process.output);
|
||||
} finally {
|
||||
generated.parent.delete(recursive: true);
|
||||
}
|
||||
return process.toResult();
|
||||
}
|
||||
}
|
||||
|
||||
main(List<String> arguments) => runMe(arguments, createContext, "testing.json");
|
28
pkg/kernel/test/closures/testing.json
Normal file
28
pkg/kernel/test/closures/testing.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"":"Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file",
|
||||
"":"for details. All rights reserved. Use of this source code is governed by a",
|
||||
"":"BSD-style license that can be found in the LICENSE.md file.",
|
||||
"packages": "../../../../.packages",
|
||||
"suites": [
|
||||
{
|
||||
"name": "closures",
|
||||
"kind": "Chain",
|
||||
"source": "suite.dart",
|
||||
"path": "../../testcases/closures",
|
||||
"status": "closures.status",
|
||||
"pattern": [
|
||||
"\\.dart$"
|
||||
],
|
||||
"exclude": [
|
||||
"/test/closures/suite\\.dart$"
|
||||
]
|
||||
}
|
||||
],
|
||||
"analyze": {
|
||||
"uris": [
|
||||
"suite.dart"
|
||||
],
|
||||
"exclude": [
|
||||
]
|
||||
}
|
||||
}
|
15
pkg/kernel/test/closures_test.dart
Normal file
15
pkg/kernel/test/closures_test.dart
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
library test.kernel.closures_test;
|
||||
|
||||
import 'package:test/test.dart' show Timeout, test;
|
||||
|
||||
import 'package:testing/testing.dart' show run;
|
||||
|
||||
main() {
|
||||
test("closures",
|
||||
() => run([], ["closures"], "pkg/kernel/test/closures/testing.json"),
|
||||
timeout: new Timeout(new Duration(minutes: 5)));
|
||||
}
|
|
@ -53,7 +53,6 @@ class BinaryPrinterWithExpectedOutput extends BinaryPrinter {
|
|||
BinaryPrinterWithExpectedOutput(this.expectedBytes)
|
||||
: super(new IOSink(new DummyStreamConsumer()));
|
||||
|
||||
|
||||
String show(int byte) {
|
||||
if (byte == eof) return 'EOF';
|
||||
return '$byte (0x${byte.toRadixString(16).padLeft(2, "0")})';
|
||||
|
|
|
@ -12,9 +12,8 @@ import 'package:args/args.dart';
|
|||
import 'dart:io';
|
||||
|
||||
ArgParser argParser = new ArgParser()
|
||||
..addOption('count', abbr: 'c',
|
||||
help: 'Build N copies of the tree shaker',
|
||||
defaultsTo: '100');
|
||||
..addOption('count',
|
||||
abbr: 'c', help: 'Build N copies of the tree shaker', defaultsTo: '100');
|
||||
|
||||
String usage = """
|
||||
Usage: treeshaker_membench [options] FILE.dart
|
||||
|
|
|
@ -105,6 +105,7 @@ main(List<String> args) {
|
|||
String dotCode = visualizer.dumpMember(member);
|
||||
new File(path).writeAsStringSync(dotCode);
|
||||
}
|
||||
|
||||
for (var library in program.libraries) {
|
||||
library.members.forEach(dumpMember);
|
||||
for (var class_ in library.classes) {
|
||||
|
|
|
@ -155,9 +155,7 @@ class SelfCheckTransformer {
|
|||
// is allowed to occur. We use this because it is hard to check directly
|
||||
// that a value is of the 'other' type.
|
||||
bool disallowOtherValues = expected.valueBits & ValueBit.other == 0;
|
||||
List<Expression> anyChecks = disallowOtherValues
|
||||
? <Expression>[]
|
||||
: null;
|
||||
List<Expression> anyChecks = disallowOtherValues ? <Expression>[] : null;
|
||||
|
||||
void checkType(int bit, DartType type) {
|
||||
if (expected.valueBits & bit == 0) {
|
||||
|
|
|
@ -43,8 +43,10 @@ final List<TestCase> testCases = <TestCase>[
|
|||
|
||||
successCase('(x:int,y:String) => int', '(y:String,x:int) => int', {}),
|
||||
successCase('<S,T>(x:S,y:T) => S', '<S,T>(y:T,x:S) => S', {}),
|
||||
successCase('(x:<T>(T)=>T,y:<S>(S)=>S) => int', '(y:<S>(S)=>S,x:<T>(T)=>T) => int', {}),
|
||||
successCase('(x:<T>(T)=>T,y:<S>(S,S,S)=>S) => int', '(y:<S>(S,S,S)=>S,x:<T>(T)=>T) => int', {}),
|
||||
successCase('(x:<T>(T)=>T,y:<S>(S)=>S) => int',
|
||||
'(y:<S>(S)=>S,x:<T>(T)=>T) => int', {}),
|
||||
successCase('(x:<T>(T)=>T,y:<S>(S,S,S)=>S) => int',
|
||||
'(y:<S>(S,S,S)=>S,x:<T>(T)=>T) => int', {}),
|
||||
];
|
||||
|
||||
class TestCase {
|
||||
|
|
|
@ -58,7 +58,6 @@ bias.right: ${formatTime(rightBiasTime)}
|
|||
''');
|
||||
}
|
||||
|
||||
|
||||
String formatTime(int microseconds) {
|
||||
double seconds = microseconds / 1000000.0;
|
||||
return '$seconds s';
|
||||
|
|
9
pkg/kernel/testcases/closures/README.md
Normal file
9
pkg/kernel/testcases/closures/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
<!--
|
||||
Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
for details. All rights reserved. Use of this source code is governed by a
|
||||
BSD-style license that can be found in the LICENSE file.
|
||||
-->
|
||||
# Running tests
|
||||
|
||||
export DART_AOT_SDK=.../xcodebuild/DerivedSources/DebugX64/patched_sdk
|
||||
dart -c --packages=.packages package:testing/src/run_tests.dart test/closures/testing.json
|
9
pkg/kernel/testcases/closures/capture_closure.dart
Normal file
9
pkg/kernel/testcases/closures/capture_closure.dart
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
main(arguments) {
|
||||
f() => null;
|
||||
g() => f();
|
||||
g();
|
||||
}
|
36
pkg/kernel/testcases/closures/capture_closure.dart.expect
Normal file
36
pkg/kernel/testcases/closures/capture_closure.dart.expect
Normal file
|
@ -0,0 +1,36 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#f extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#f::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#f::context};
|
||||
return null;
|
||||
}
|
||||
}
|
||||
class Closure#main#g extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#g::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#g::context};
|
||||
return #contextParameter.[](0).call();
|
||||
}
|
||||
}
|
||||
static method main(dynamic arguments) → dynamic {
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, new self::Closure#main#f::•(#context));
|
||||
final dynamic g = new self::Closure#main#g::•(#context);
|
||||
g.call();
|
||||
}
|
13
pkg/kernel/testcases/closures/capture_closure_parameter.dart
Normal file
13
pkg/kernel/testcases/closures/capture_closure_parameter.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
main(List<String> arguments) {
|
||||
foo(x) {
|
||||
bar() {
|
||||
print(x);
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
foo(arguments[0])();
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#foo#bar extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#foo#bar::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#foo#bar::context};
|
||||
core::print(#contextParameter.[](0));
|
||||
}
|
||||
}
|
||||
class Closure#main#foo extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#foo::context = context
|
||||
;
|
||||
method call(dynamic x) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#foo::context};
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = #contextParameter;
|
||||
#context.[]=(0, null);
|
||||
final dynamic bar = new self::Closure#main#foo#bar::•(#context);
|
||||
return bar;
|
||||
}
|
||||
}
|
||||
static method main(core::List<core::String> arguments) → dynamic {
|
||||
final dynamic foo = new self::Closure#main#foo::•(null);
|
||||
foo.call(arguments.[](0)).call();
|
||||
}
|
18
pkg/kernel/testcases/closures/capture_this.dart
Normal file
18
pkg/kernel/testcases/closures/capture_this.dart
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
class C {
|
||||
var x;
|
||||
m() => (v) => x = v;
|
||||
f() => () => () => x;
|
||||
}
|
||||
|
||||
main() {
|
||||
C c = new C();
|
||||
c.x = 41;
|
||||
c.m()(42);
|
||||
if (42 != c.x) throw "Unexpected value in c.x: ${c.x}";
|
||||
var result = c.f()()();
|
||||
if (42 != result) throw "Unexpected value from c.f()()(): $result";
|
||||
}
|
69
pkg/kernel/testcases/closures/capture_this.dart.expect
Normal file
69
pkg/kernel/testcases/closures/capture_this.dart.expect
Normal file
|
@ -0,0 +1,69 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class C extends core::Object {
|
||||
field dynamic x = null;
|
||||
constructor •() → void
|
||||
: super core::Object::•()
|
||||
;
|
||||
method m() → dynamic {
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, this);
|
||||
return new self::Closure#C#m#function::•(#context);
|
||||
}
|
||||
method f() → dynamic {
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, this);
|
||||
return new self::Closure#C#f#function::•(#context);
|
||||
}
|
||||
}
|
||||
class Closure#C#m#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#m#function::context = context
|
||||
;
|
||||
method call(dynamic v) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#m#function::context};
|
||||
return #contextParameter.[](0).x = v;
|
||||
}
|
||||
}
|
||||
class Closure#C#f#function#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#f#function#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#f#function#function::context};
|
||||
return #contextParameter.[](0).x;
|
||||
}
|
||||
}
|
||||
class Closure#C#f#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#f#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#f#function::context};
|
||||
return new self::Closure#C#f#function#function::•(#contextParameter);
|
||||
}
|
||||
}
|
||||
static method main() → dynamic {
|
||||
self::C c = new self::C::•();
|
||||
c.x = 41;
|
||||
c.m().call(42);
|
||||
if(!42.==(c.x))
|
||||
throw "Unexpected value in c.x: ${c.x}";
|
||||
dynamic result = c.f().call().call();
|
||||
if(!42.==(result))
|
||||
throw "Unexpected value from c.f()()(): ${result}";
|
||||
}
|
17
pkg/kernel/testcases/closures/catch.dart
Normal file
17
pkg/kernel/testcases/closures/catch.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
main() {
|
||||
var c;
|
||||
try {
|
||||
throw "Fisk";
|
||||
} on String catch (e, s) {
|
||||
c = () {
|
||||
print(e);
|
||||
if (s != null) print(s);
|
||||
};
|
||||
}
|
||||
c();
|
||||
print("TEST PASSED");
|
||||
}
|
34
pkg/kernel/testcases/closures/catch.dart.expect
Normal file
34
pkg/kernel/testcases/closures/catch.dart.expect
Normal file
|
@ -0,0 +1,34 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#function::context};
|
||||
core::print(#contextParameter.[](0));
|
||||
if(!#contextParameter.[](1).==(null))
|
||||
core::print(#contextParameter.[](1));
|
||||
}
|
||||
}
|
||||
static method main() → dynamic {
|
||||
dynamic c;
|
||||
try {
|
||||
throw "Fisk";
|
||||
}
|
||||
on core::String catch(dynamic #t1, dynamic #t2) {
|
||||
final mock::Context #context = new mock::Context::•(2);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, #t1);
|
||||
#context.[]=(1, #t2);
|
||||
c = new self::Closure#main#function::•(#context);
|
||||
}
|
||||
c.call();
|
||||
core::print("TEST PASSED");
|
||||
}
|
14
pkg/kernel/testcases/closures/closures.dart
Normal file
14
pkg/kernel/testcases/closures/closures.dart
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
var f;
|
||||
|
||||
foo() {
|
||||
print(f(0));
|
||||
}
|
||||
|
||||
main(arguments) {
|
||||
f = (x) => arguments[x];
|
||||
foo();
|
||||
}
|
28
pkg/kernel/testcases/closures/closures.dart.expect
Normal file
28
pkg/kernel/testcases/closures/closures.dart.expect
Normal file
|
@ -0,0 +1,28 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#function::context = context
|
||||
;
|
||||
method call(dynamic x) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#function::context};
|
||||
return #contextParameter.[](0).[](x);
|
||||
}
|
||||
}
|
||||
static field dynamic f = null;
|
||||
static method foo() → dynamic {
|
||||
core::print(self::f.call(0));
|
||||
}
|
||||
static method main(dynamic arguments) → dynamic {
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, arguments);
|
||||
self::f = new self::Closure#main#function::•(#context);
|
||||
self::foo();
|
||||
}
|
25
pkg/kernel/testcases/closures/field.dart
Normal file
25
pkg/kernel/testcases/closures/field.dart
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
var x = () => "x";
|
||||
|
||||
class C<T> {
|
||||
var v = (x) => x is T;
|
||||
|
||||
final y = () => "y";
|
||||
|
||||
static final z = () => "z";
|
||||
}
|
||||
|
||||
main() {
|
||||
if (!new C<String>().v("")) throw "C<String>.v false on String";
|
||||
if (new C<String>().v(0)) throw "C<String>.v true on int";
|
||||
if (new C<String>().v(null)) throw "C<String>.v true on null";
|
||||
if (new C<int>().v("")) throw "C<int>.v true on String";
|
||||
if (!new C<int>().v(0)) throw "C<int>.v false on int";
|
||||
if (new C<int>().v(null)) throw "C<int>.v true on null";
|
||||
if ("x" != x()) throw "x";
|
||||
if ("y" != new C<String>().y()) throw "y";
|
||||
if ("z" != C.z()) throw "z";
|
||||
}
|
82
pkg/kernel/testcases/closures/field.dart.expect
Normal file
82
pkg/kernel/testcases/closures/field.dart.expect
Normal file
|
@ -0,0 +1,82 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class C<T extends core::Object> extends core::Object {
|
||||
field dynamic v = new self::Closure#C#v#function::•<self::C::T>(null);
|
||||
final field dynamic y = new self::Closure#C#y#function::•(null);
|
||||
static final field dynamic z = new self::Closure#C#z#function::•(null);
|
||||
constructor •() → void
|
||||
: super core::Object::•()
|
||||
;
|
||||
}
|
||||
class Closure#C#v#function<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#v#function::context = context
|
||||
;
|
||||
method call(dynamic x) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#v#function::context};
|
||||
return x is self::Closure#C#v#function::T;
|
||||
}
|
||||
}
|
||||
class Closure#C#y#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#y#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#y#function::context};
|
||||
return "y";
|
||||
}
|
||||
}
|
||||
class Closure#C#z#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#z#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#z#function::context};
|
||||
return "z";
|
||||
}
|
||||
}
|
||||
class Closure#x#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#x#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#x#function::context};
|
||||
return "x";
|
||||
}
|
||||
}
|
||||
static field dynamic x = new self::Closure#x#function::•(null);
|
||||
static method main() → dynamic {
|
||||
if(!new self::C::•<core::String>().v(""))
|
||||
throw "C<String>.v false on String";
|
||||
if(new self::C::•<core::String>().v(0))
|
||||
throw "C<String>.v true on int";
|
||||
if(new self::C::•<core::String>().v(null))
|
||||
throw "C<String>.v true on null";
|
||||
if(new self::C::•<core::int>().v(""))
|
||||
throw "C<int>.v true on String";
|
||||
if(!new self::C::•<core::int>().v(0))
|
||||
throw "C<int>.v false on int";
|
||||
if(new self::C::•<core::int>().v(null))
|
||||
throw "C<int>.v true on null";
|
||||
if(!"x".==(self::x.call()))
|
||||
throw "x";
|
||||
if(!"y".==(new self::C::•<core::String>().y()))
|
||||
throw "y";
|
||||
if(!"z".==(self::C::z.call()))
|
||||
throw "z";
|
||||
}
|
21
pkg/kernel/testcases/closures/for_in_closure.dart
Normal file
21
pkg/kernel/testcases/closures/for_in_closure.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
const numbers = const <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
main() {
|
||||
var closures = [];
|
||||
for (int i in numbers) {
|
||||
closures.add(() => i);
|
||||
}
|
||||
int sum = 0;
|
||||
for (Function f in closures) {
|
||||
sum += f();
|
||||
}
|
||||
// This formula is credited to Gauss. Search for "Gauss adding 1 to 100".
|
||||
int expectedSum = (numbers.length - 1) * numbers.length ~/ 2;
|
||||
if (expectedSum != sum) {
|
||||
throw new Exception("Unexpected sum = $sum != $expectedSum");
|
||||
}
|
||||
}
|
37
pkg/kernel/testcases/closures/for_in_closure.dart.expect
Normal file
37
pkg/kernel/testcases/closures/for_in_closure.dart.expect
Normal file
|
@ -0,0 +1,37 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#function::context};
|
||||
return #contextParameter.[](0);
|
||||
}
|
||||
}
|
||||
static const field dynamic numbers = const <core::int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
static method main() → dynamic {
|
||||
dynamic closures = <dynamic>[];
|
||||
for (core::int i in self::numbers) {
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, i);
|
||||
{
|
||||
closures.add(new self::Closure#main#function::•(#context));
|
||||
}
|
||||
}
|
||||
core::int sum = 0;
|
||||
for (core::Function f in closures) {
|
||||
sum = sum.+(f.call());
|
||||
}
|
||||
core::int expectedSum = self::numbers.length.-(1).*(self::numbers.length).~/(2);
|
||||
if(!expectedSum.==(sum)) {
|
||||
throw core::Exception::•("Unexpected sum = ${sum} != ${expectedSum}");
|
||||
}
|
||||
}
|
29
pkg/kernel/testcases/closures/for_loop.dart
Normal file
29
pkg/kernel/testcases/closures/for_loop.dart
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
const int max = 100;
|
||||
|
||||
main() {
|
||||
var closures = [];
|
||||
var closures2 = [];
|
||||
var last;
|
||||
for (int i = 0; i < max; i++) {
|
||||
closures.add(() => last = i);
|
||||
closures2.add(() {
|
||||
if (last != max - 1) throw "last: $last != ${max - 1}";
|
||||
});
|
||||
}
|
||||
int sum = 0;
|
||||
for (Function f in closures) {
|
||||
sum += f();
|
||||
}
|
||||
for (Function f in closures2) {
|
||||
f();
|
||||
}
|
||||
// This formula is credited to Gauss. Search for "Gauss adding 1 to 100".
|
||||
int expectedSum = (max - 1) * max ~/ 2;
|
||||
if (expectedSum != sum) {
|
||||
throw new Exception("Unexpected sum = $sum != $expectedSum");
|
||||
}
|
||||
}
|
58
pkg/kernel/testcases/closures/for_loop.dart.expect
Normal file
58
pkg/kernel/testcases/closures/for_loop.dart.expect
Normal file
|
@ -0,0 +1,58 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#function::context};
|
||||
return let final dynamic #t1 = #contextParameter.parent in let final dynamic #t2 = 0 in let final dynamic #t3 = #contextParameter.[](0) in let final dynamic #t4 = #t1.[]=(#t2, #t3) in #t3;
|
||||
}
|
||||
}
|
||||
class Closure#main#function#1 extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#function#1::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#function#1::context};
|
||||
if(!#contextParameter.parent.[](0).==(self::max.-(1)))
|
||||
throw "last: ${#contextParameter.parent.[](0)} != ${self::max.-(1)}";
|
||||
}
|
||||
}
|
||||
static const field core::int max = 100;
|
||||
static method main() → dynamic {
|
||||
dynamic closures = <dynamic>[];
|
||||
dynamic closures2 = <dynamic>[];
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, null);
|
||||
{
|
||||
mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = #context;
|
||||
#context.[]=(0, 0);
|
||||
for (; #context.[](0).<(self::max); #context = #context.copy(), #context.[]=(0, #context.[](0).+(1))) {
|
||||
closures.add(new self::Closure#main#function::•(#context));
|
||||
closures2.add(new self::Closure#main#function#1::•(#context));
|
||||
}
|
||||
}
|
||||
core::int sum = 0;
|
||||
for (core::Function f in closures) {
|
||||
sum = sum.+(f.call());
|
||||
}
|
||||
for (core::Function f in closures2) {
|
||||
f.call();
|
||||
}
|
||||
core::int expectedSum = self::max.-(1).*(self::max).~/(2);
|
||||
if(!expectedSum.==(sum)) {
|
||||
throw core::Exception::•("Unexpected sum = ${sum} != ${expectedSum}");
|
||||
}
|
||||
}
|
15
pkg/kernel/testcases/closures/for_variable_capture_test.dart
Normal file
15
pkg/kernel/testcases/closures/for_variable_capture_test.dart
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
main() {
|
||||
var closure;
|
||||
for (var i=0, fn = () => i; i < 3; i++) {
|
||||
i += 1;
|
||||
closure = fn;
|
||||
}
|
||||
var x = closure();
|
||||
if (x != 1) {
|
||||
throw "Expected 1, but got $x.";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#function::context};
|
||||
return #contextParameter.[](0);
|
||||
}
|
||||
}
|
||||
static method main() → dynamic {
|
||||
dynamic closure;
|
||||
{
|
||||
mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, 0);
|
||||
dynamic fn = new self::Closure#main#function::•(#context);
|
||||
for (; #context.[](0).<(3); #context = #context.copy(), #context.[]=(0, #context.[](0).+(1))) {
|
||||
#context.[]=(0, #context.[](0).+(1));
|
||||
closure = fn;
|
||||
}
|
||||
}
|
||||
dynamic x = closure.call();
|
||||
if(!x.==(1)) {
|
||||
throw "Expected 1, but got ${x}.";
|
||||
}
|
||||
}
|
65
pkg/kernel/testcases/closures/instance_tear_off.dart
Normal file
65
pkg/kernel/testcases/closures/instance_tear_off.dart
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
class C {
|
||||
var f = () => "f";
|
||||
get g => (x) => "g($x)";
|
||||
a() => "a";
|
||||
b(x) => x;
|
||||
c(x, [y = 2]) => x + y;
|
||||
d(x, {y: 2}) => x + y;
|
||||
}
|
||||
|
||||
/// This class doesn't use its type variable.
|
||||
class D<T> {
|
||||
var f = () => "f";
|
||||
get g => (x) => "g($x)";
|
||||
a() => "a";
|
||||
b(x) => x;
|
||||
c(x, [y = 2]) => x + y;
|
||||
d(x, {y: 2}) => x + y;
|
||||
}
|
||||
|
||||
/// This class uses its type variable.
|
||||
class E<T> {
|
||||
var f = () => "f";
|
||||
get g => (T x) => "g($x)";
|
||||
a() => "a";
|
||||
b(T x) => x;
|
||||
c(T x, [T y = 2]) => x + y;
|
||||
d(T x, {T y: 2}) => x + y;
|
||||
}
|
||||
|
||||
expect(expected, actual) {
|
||||
print("Expecting '$expected' and got '$actual'");
|
||||
if (expected != actual) {
|
||||
print("Expected '$expected' but got '$actual'");
|
||||
throw "Expected '$expected' but got '$actual'";
|
||||
}
|
||||
}
|
||||
|
||||
test(o) {
|
||||
expect("f", o.f());
|
||||
expect("f", (o.f)());
|
||||
expect("g(42)", o.g(42));
|
||||
expect("g(42)", (o.g)(42));
|
||||
expect("a", o.a());
|
||||
expect("a", (o.a)());
|
||||
expect(42, o.b(42));
|
||||
expect(42, (o.b)(42));
|
||||
expect(42, o.c(40));
|
||||
expect(42, (o.c)(40));
|
||||
expect(87, o.c(80, 7));
|
||||
expect(87, (o.c)(80, 7));
|
||||
expect(42, o.d(40));
|
||||
expect(42, (o.d)(40));
|
||||
expect(87, o.d(80, y: 7));
|
||||
expect(87, (o.d)(80, y: 7));
|
||||
}
|
||||
|
||||
main(arguments) {
|
||||
test(new C());
|
||||
test(new D<int>());
|
||||
test(new E<int>());
|
||||
}
|
302
pkg/kernel/testcases/closures/instance_tear_off.dart.expect
Normal file
302
pkg/kernel/testcases/closures/instance_tear_off.dart.expect
Normal file
|
@ -0,0 +1,302 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class C extends core::Object {
|
||||
field dynamic f = new self::Closure#C#f#function::•(null);
|
||||
constructor •() → void
|
||||
: super core::Object::•()
|
||||
;
|
||||
get g() → dynamic {
|
||||
return new self::Closure#C#g#function::•(null);
|
||||
}
|
||||
method a() → dynamic {
|
||||
return "a";
|
||||
}
|
||||
method b(dynamic x) → dynamic {
|
||||
return x;
|
||||
}
|
||||
method c(dynamic x, [dynamic y = 2]) → dynamic {
|
||||
return x.+(y);
|
||||
}
|
||||
method d(dynamic x, {dynamic y = 2}) → dynamic {
|
||||
return x.+(y);
|
||||
}
|
||||
get a#get() → dynamic
|
||||
return new self::Closure#C#a::•(this);
|
||||
get b#get() → dynamic
|
||||
return new self::Closure#C#b::•(this);
|
||||
get c#get() → dynamic
|
||||
return new self::Closure#C#c::•(this);
|
||||
get d#get() → dynamic
|
||||
return new self::Closure#C#d::•(this);
|
||||
}
|
||||
class D<T extends core::Object> extends core::Object {
|
||||
field dynamic f = new self::Closure#D#f#function::•(null);
|
||||
constructor •() → void
|
||||
: super core::Object::•()
|
||||
;
|
||||
get g() → dynamic {
|
||||
return new self::Closure#D#g#function::•(null);
|
||||
}
|
||||
method a() → dynamic {
|
||||
return "a";
|
||||
}
|
||||
method b(dynamic x) → dynamic {
|
||||
return x;
|
||||
}
|
||||
method c(dynamic x, [dynamic y = 2]) → dynamic {
|
||||
return x.+(y);
|
||||
}
|
||||
method d(dynamic x, {dynamic y = 2}) → dynamic {
|
||||
return x.+(y);
|
||||
}
|
||||
get a#get() → dynamic
|
||||
return new self::Closure#D#a::•<self::D::T>(this);
|
||||
get b#get() → dynamic
|
||||
return new self::Closure#D#b::•<self::D::T>(this);
|
||||
get c#get() → dynamic
|
||||
return new self::Closure#D#c::•<self::D::T>(this);
|
||||
get d#get() → dynamic
|
||||
return new self::Closure#D#d::•<self::D::T>(this);
|
||||
}
|
||||
class E<T extends core::Object> extends core::Object {
|
||||
field dynamic f = new self::Closure#E#f#function::•(null);
|
||||
constructor •() → void
|
||||
: super core::Object::•()
|
||||
;
|
||||
get g() → dynamic {
|
||||
return new self::Closure#E#g#function::•<self::E::T>(null);
|
||||
}
|
||||
method a() → dynamic {
|
||||
return "a";
|
||||
}
|
||||
method b(self::E::T x) → dynamic {
|
||||
return x;
|
||||
}
|
||||
method c(self::E::T x, [self::E::T y = 2]) → dynamic {
|
||||
return x.+(y);
|
||||
}
|
||||
method d(self::E::T x, {self::E::T y = 2}) → dynamic {
|
||||
return x.+(y);
|
||||
}
|
||||
get a#get() → dynamic
|
||||
return new self::Closure#E#a::•<self::E::T>(this);
|
||||
get b#get() → dynamic
|
||||
return new self::Closure#E#b::•<self::E::T>(this);
|
||||
get c#get() → dynamic
|
||||
return new self::Closure#E#c::•<self::E::T>(this);
|
||||
get d#get() → dynamic
|
||||
return new self::Closure#E#d::•<self::E::T>(this);
|
||||
}
|
||||
class Closure#C#g#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#g#function::context = context
|
||||
;
|
||||
method call(dynamic x) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#g#function::context};
|
||||
return "g(${x})";
|
||||
}
|
||||
}
|
||||
class Closure#C#a extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::C self;
|
||||
constructor •(final self::C self) → dynamic
|
||||
: self::Closure#C#a::self = self
|
||||
;
|
||||
method call() → dynamic
|
||||
return this.{self::Closure#C#a::self}.{self::C::a}();
|
||||
}
|
||||
class Closure#C#b extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::C self;
|
||||
constructor •(final self::C self) → dynamic
|
||||
: self::Closure#C#b::self = self
|
||||
;
|
||||
method call(dynamic x) → dynamic
|
||||
return this.{self::Closure#C#b::self}.{self::C::b}(x);
|
||||
}
|
||||
class Closure#C#c extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::C self;
|
||||
constructor •(final self::C self) → dynamic
|
||||
: self::Closure#C#c::self = self
|
||||
;
|
||||
method call(dynamic x, [dynamic y = 2]) → dynamic
|
||||
return this.{self::Closure#C#c::self}.{self::C::c}(x, y);
|
||||
}
|
||||
class Closure#C#d extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::C self;
|
||||
constructor •(final self::C self) → dynamic
|
||||
: self::Closure#C#d::self = self
|
||||
;
|
||||
method call(dynamic x, {dynamic y = 2}) → dynamic
|
||||
return this.{self::Closure#C#d::self}.{self::C::d}(x, y: y);
|
||||
}
|
||||
class Closure#C#f#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#f#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#f#function::context};
|
||||
return "f";
|
||||
}
|
||||
}
|
||||
class Closure#D#g#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#D#g#function::context = context
|
||||
;
|
||||
method call(dynamic x) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#D#g#function::context};
|
||||
return "g(${x})";
|
||||
}
|
||||
}
|
||||
class Closure#D#a<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::D<self::Closure#D#a::T> self;
|
||||
constructor •(final self::D<self::Closure#D#a::T> self) → dynamic
|
||||
: self::Closure#D#a::self = self
|
||||
;
|
||||
method call() → dynamic
|
||||
return this.{self::Closure#D#a::self}.{self::D::a}();
|
||||
}
|
||||
class Closure#D#b<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::D<self::Closure#D#b::T> self;
|
||||
constructor •(final self::D<self::Closure#D#b::T> self) → dynamic
|
||||
: self::Closure#D#b::self = self
|
||||
;
|
||||
method call(dynamic x) → dynamic
|
||||
return this.{self::Closure#D#b::self}.{self::D::b}(x);
|
||||
}
|
||||
class Closure#D#c<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::D<self::Closure#D#c::T> self;
|
||||
constructor •(final self::D<self::Closure#D#c::T> self) → dynamic
|
||||
: self::Closure#D#c::self = self
|
||||
;
|
||||
method call(dynamic x, [dynamic y = 2]) → dynamic
|
||||
return this.{self::Closure#D#c::self}.{self::D::c}(x, y);
|
||||
}
|
||||
class Closure#D#d<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::D<self::Closure#D#d::T> self;
|
||||
constructor •(final self::D<self::Closure#D#d::T> self) → dynamic
|
||||
: self::Closure#D#d::self = self
|
||||
;
|
||||
method call(dynamic x, {dynamic y = 2}) → dynamic
|
||||
return this.{self::Closure#D#d::self}.{self::D::d}(x, y: y);
|
||||
}
|
||||
class Closure#D#f#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#D#f#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#D#f#function::context};
|
||||
return "f";
|
||||
}
|
||||
}
|
||||
class Closure#E#g#function<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#E#g#function::context = context
|
||||
;
|
||||
method call(self::Closure#E#g#function::T x) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#E#g#function::context};
|
||||
return "g(${x})";
|
||||
}
|
||||
}
|
||||
class Closure#E#a<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::E<self::Closure#E#a::T> self;
|
||||
constructor •(final self::E<self::Closure#E#a::T> self) → dynamic
|
||||
: self::Closure#E#a::self = self
|
||||
;
|
||||
method call() → dynamic
|
||||
return this.{self::Closure#E#a::self}.{self::E::a}();
|
||||
}
|
||||
class Closure#E#b<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::E<self::Closure#E#b::T> self;
|
||||
constructor •(final self::E<self::Closure#E#b::T> self) → dynamic
|
||||
: self::Closure#E#b::self = self
|
||||
;
|
||||
method call(self::Closure#E#b::T x) → dynamic
|
||||
return this.{self::Closure#E#b::self}.{self::E::b}(x);
|
||||
}
|
||||
class Closure#E#c<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::E<self::Closure#E#c::T> self;
|
||||
constructor •(final self::E<self::Closure#E#c::T> self) → dynamic
|
||||
: self::Closure#E#c::self = self
|
||||
;
|
||||
method call(self::Closure#E#c::T x, [self::Closure#E#c::T y = 2]) → dynamic
|
||||
return this.{self::Closure#E#c::self}.{self::E::c}(x, y);
|
||||
}
|
||||
class Closure#E#d<T extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field self::E<self::Closure#E#d::T> self;
|
||||
constructor •(final self::E<self::Closure#E#d::T> self) → dynamic
|
||||
: self::Closure#E#d::self = self
|
||||
;
|
||||
method call(self::Closure#E#d::T x, {self::Closure#E#d::T y = 2}) → dynamic
|
||||
return this.{self::Closure#E#d::self}.{self::E::d}(x, y: y);
|
||||
}
|
||||
class Closure#E#f#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#E#f#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#E#f#function::context};
|
||||
return "f";
|
||||
}
|
||||
}
|
||||
static method expect(dynamic expected, dynamic actual) → dynamic {
|
||||
core::print("Expecting '${expected}' and got '${actual}'");
|
||||
if(!expected.==(actual)) {
|
||||
core::print("Expected '${expected}' but got '${actual}'");
|
||||
throw "Expected '${expected}' but got '${actual}'";
|
||||
}
|
||||
}
|
||||
static method test(dynamic o) → dynamic {
|
||||
self::expect("f", o.f());
|
||||
self::expect("f", o.f.call());
|
||||
self::expect("g(42)", o.g(42));
|
||||
self::expect("g(42)", o.g.call(42));
|
||||
self::expect("a", o.a());
|
||||
self::expect("a", o.a#get.call());
|
||||
self::expect(42, o.b(42));
|
||||
self::expect(42, o.b#get.call(42));
|
||||
self::expect(42, o.c(40));
|
||||
self::expect(42, o.c#get.call(40));
|
||||
self::expect(87, o.c(80, 7));
|
||||
self::expect(87, o.c#get.call(80, 7));
|
||||
self::expect(42, o.d(40));
|
||||
self::expect(42, o.d#get.call(40));
|
||||
self::expect(87, o.d(80, y: 7));
|
||||
self::expect(87, o.d#get.call(80, y: 7));
|
||||
}
|
||||
static method main(dynamic arguments) → dynamic {
|
||||
self::test(new self::C::•());
|
||||
self::test(new self::D::•<core::int>());
|
||||
self::test(new self::E::•<core::int>());
|
||||
}
|
15
pkg/kernel/testcases/closures/named_closure.dart
Normal file
15
pkg/kernel/testcases/closures/named_closure.dart
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
var f;
|
||||
|
||||
foo() {
|
||||
print(f(0));
|
||||
}
|
||||
|
||||
main(arguments) {
|
||||
g(x) => arguments[x];
|
||||
f = g;
|
||||
foo();
|
||||
}
|
29
pkg/kernel/testcases/closures/named_closure.dart.expect
Normal file
29
pkg/kernel/testcases/closures/named_closure.dart.expect
Normal file
|
@ -0,0 +1,29 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#g extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#g::context = context
|
||||
;
|
||||
method call(dynamic x) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#g::context};
|
||||
return #contextParameter.[](0).[](x);
|
||||
}
|
||||
}
|
||||
static field dynamic f = null;
|
||||
static method foo() → dynamic {
|
||||
core::print(self::f.call(0));
|
||||
}
|
||||
static method main(dynamic arguments) → dynamic {
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, arguments);
|
||||
final dynamic g = new self::Closure#main#g::•(#context);
|
||||
self::f = g;
|
||||
self::foo();
|
||||
}
|
28
pkg/kernel/testcases/closures/non_void_context.dart
Normal file
28
pkg/kernel/testcases/closures/non_void_context.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
var v;
|
||||
|
||||
main(arguments) {
|
||||
var w;
|
||||
((x) => v = w = x)(87);
|
||||
if (v != 87) {
|
||||
throw "Unexpected value in v: $v";
|
||||
}
|
||||
if (w != 87) {
|
||||
throw "Unexpected value in w: $w";
|
||||
}
|
||||
v = true;
|
||||
(() {
|
||||
for (; w = v;) {
|
||||
v = false;
|
||||
}
|
||||
})();
|
||||
if (v != false) {
|
||||
throw "Unexpected value in v: $v";
|
||||
}
|
||||
if (w != false) {
|
||||
throw "Unexpected value in w: $w";
|
||||
}
|
||||
}
|
52
pkg/kernel/testcases/closures/non_void_context.dart.expect
Normal file
52
pkg/kernel/testcases/closures/non_void_context.dart.expect
Normal file
|
@ -0,0 +1,52 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#function::context = context
|
||||
;
|
||||
method call(dynamic x) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#function::context};
|
||||
return self::v = let final dynamic #t1 = #contextParameter in let final dynamic #t2 = 0 in let final dynamic #t3 = x in let final dynamic #t4 = #t1.[]=(#t2, #t3) in #t3;
|
||||
}
|
||||
}
|
||||
class Closure#main#function#1 extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#function#1::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#function#1::context};
|
||||
for (; let final dynamic #t5 = #contextParameter in let final dynamic #t6 = 0 in let final dynamic #t7 = self::v in let final dynamic #t8 = #t5.[]=(#t6, #t7) in #t7; ) {
|
||||
self::v = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
static field dynamic v = null;
|
||||
static method main(dynamic arguments) → dynamic {
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, null);
|
||||
new self::Closure#main#function::•(#context).call(87);
|
||||
if(!self::v.==(87)) {
|
||||
throw "Unexpected value in v: ${self::v}";
|
||||
}
|
||||
if(!#context.[](0).==(87)) {
|
||||
throw "Unexpected value in w: ${#context.[](0)}";
|
||||
}
|
||||
self::v = true;
|
||||
new self::Closure#main#function#1::•(#context).call();
|
||||
if(!self::v.==(false)) {
|
||||
throw "Unexpected value in v: ${self::v}";
|
||||
}
|
||||
if(!#context.[](0).==(false)) {
|
||||
throw "Unexpected value in w: ${#context.[](0)}";
|
||||
}
|
||||
}
|
48
pkg/kernel/testcases/closures/static_tear_off.dart
Normal file
48
pkg/kernel/testcases/closures/static_tear_off.dart
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
f_1_1_no_default(a, [b]) => a + b;
|
||||
|
||||
f_1_1_default(a, [b = 2]) => a + b;
|
||||
|
||||
f_1_b_no_default(a, {b}) => a + b;
|
||||
|
||||
f_1_b_default(a, {b: 2}) => a + b;
|
||||
|
||||
test_1_1(Function f, bool hasDefault) {
|
||||
var result = f(40, 2);
|
||||
if (42 != result) throw "Unexpected result: $result";
|
||||
test_1(f, hasDefault);
|
||||
}
|
||||
|
||||
test_1_b(Function f, bool hasDefault) {
|
||||
var result = f(40, b: 2);
|
||||
if (42 != result) throw "Unexpected result: $result";
|
||||
test_1(f, hasDefault);
|
||||
}
|
||||
|
||||
test_1(Function f, bool hasDefault) {
|
||||
var result = 0;
|
||||
bool threw = true;
|
||||
try {
|
||||
result = f(40);
|
||||
threw = false;
|
||||
} catch (_) {
|
||||
// Ignored.
|
||||
}
|
||||
if (hasDefault) {
|
||||
if (threw) throw "Unexpected exception.";
|
||||
if (42 != result) throw "Unexpected result: $result.";
|
||||
} else {
|
||||
if (!threw) throw "Expected exception missing.";
|
||||
if (0 != result) throw "Unexpected result: $result.";
|
||||
}
|
||||
}
|
||||
|
||||
main(arguments) {
|
||||
test_1_1(f_1_1_no_default, false);
|
||||
test_1_1(f_1_1_default, true);
|
||||
test_1_b(f_1_b_no_default, false);
|
||||
test_1_b(f_1_b_default, true);
|
||||
}
|
84
pkg/kernel/testcases/closures/static_tear_off.dart.expect
Normal file
84
pkg/kernel/testcases/closures/static_tear_off.dart.expect
Normal file
|
@ -0,0 +1,84 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
class Closure#f_1_1_no_default extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
constructor •() → dynamic
|
||||
;
|
||||
method call(dynamic a, [dynamic b]) → dynamic
|
||||
return self::f_1_1_no_default(a, b);
|
||||
}
|
||||
class Closure#f_1_1_default extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
constructor •() → dynamic
|
||||
;
|
||||
method call(dynamic a, [dynamic b = 2]) → dynamic
|
||||
return self::f_1_1_default(a, b);
|
||||
}
|
||||
class Closure#f_1_b_no_default extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
constructor •() → dynamic
|
||||
;
|
||||
method call(dynamic a, {dynamic b}) → dynamic
|
||||
return self::f_1_b_no_default(a, b: b);
|
||||
}
|
||||
class Closure#f_1_b_default extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
constructor •() → dynamic
|
||||
;
|
||||
method call(dynamic a, {dynamic b = 2}) → dynamic
|
||||
return self::f_1_b_default(a, b: b);
|
||||
}
|
||||
static method f_1_1_no_default(dynamic a, [dynamic b]) → dynamic {
|
||||
return a.+(b);
|
||||
}
|
||||
static method f_1_1_default(dynamic a, [dynamic b = 2]) → dynamic {
|
||||
return a.+(b);
|
||||
}
|
||||
static method f_1_b_no_default(dynamic a, {dynamic b}) → dynamic {
|
||||
return a.+(b);
|
||||
}
|
||||
static method f_1_b_default(dynamic a, {dynamic b = 2}) → dynamic {
|
||||
return a.+(b);
|
||||
}
|
||||
static method test_1_1(core::Function f, core::bool hasDefault) → dynamic {
|
||||
dynamic result = f.call(40, 2);
|
||||
if(!42.==(result))
|
||||
throw "Unexpected result: ${result}";
|
||||
self::test_1(f, hasDefault);
|
||||
}
|
||||
static method test_1_b(core::Function f, core::bool hasDefault) → dynamic {
|
||||
dynamic result = f.call(40, b: 2);
|
||||
if(!42.==(result))
|
||||
throw "Unexpected result: ${result}";
|
||||
self::test_1(f, hasDefault);
|
||||
}
|
||||
static method test_1(core::Function f, core::bool hasDefault) → dynamic {
|
||||
dynamic result = 0;
|
||||
core::bool threw = true;
|
||||
try {
|
||||
result = f.call(40);
|
||||
threw = false;
|
||||
}
|
||||
on dynamic catch(dynamic _) {
|
||||
}
|
||||
if(hasDefault) {
|
||||
if(threw)
|
||||
throw "Unexpected exception.";
|
||||
if(!42.==(result))
|
||||
throw "Unexpected result: ${result}.";
|
||||
}
|
||||
else {
|
||||
if(!threw)
|
||||
throw "Expected exception missing.";
|
||||
if(!0.==(result))
|
||||
throw "Unexpected result: ${result}.";
|
||||
}
|
||||
}
|
||||
static method main(dynamic arguments) → dynamic {
|
||||
self::test_1_1(new self::Closure#f_1_1_no_default::•(), false);
|
||||
self::test_1_1(new self::Closure#f_1_1_default::•(), true);
|
||||
self::test_1_b(new self::Closure#f_1_b_no_default::•(), false);
|
||||
self::test_1_b(new self::Closure#f_1_b_default::•(), true);
|
||||
}
|
39
pkg/kernel/testcases/closures/type_variables.dart
Normal file
39
pkg/kernel/testcases/closures/type_variables.dart
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
class C<T, S> {
|
||||
foo(S s) => (T x) {
|
||||
T y = x;
|
||||
Object z = y;
|
||||
C<T, S> self = this;
|
||||
return z as T;
|
||||
};
|
||||
|
||||
bar() {
|
||||
C<T, S> self = this;
|
||||
}
|
||||
|
||||
baz() {
|
||||
return () => () => new C<T, S>();
|
||||
}
|
||||
|
||||
factory C() {
|
||||
local() {
|
||||
C<T, S> self = new C<T, S>.internal();
|
||||
return self;
|
||||
}
|
||||
return local();
|
||||
}
|
||||
C.internal();
|
||||
}
|
||||
|
||||
main(arguments) {
|
||||
print(new C<String, String>().foo(null)(arguments.first));
|
||||
dynamic c = new C<int, int>().baz()()();
|
||||
if (c is! C<int, int>) throw "$c fails type test 'is C<int, int>'";
|
||||
if (c is C<String, String>) {
|
||||
throw "$c passes type test 'is C<String, String>'";
|
||||
}
|
||||
print(c);
|
||||
}
|
88
pkg/kernel/testcases/closures/type_variables.dart.expect
Normal file
88
pkg/kernel/testcases/closures/type_variables.dart.expect
Normal file
|
@ -0,0 +1,88 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class C<T extends core::Object, S extends core::Object> extends core::Object {
|
||||
constructor internal() → void
|
||||
: super core::Object::•()
|
||||
;
|
||||
method foo(self::C::S s) → dynamic {
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, this);
|
||||
return new self::Closure#C#foo#function::•<self::C::T, self::C::S>(#context);
|
||||
}
|
||||
method bar() → dynamic {
|
||||
self::C<self::C::T, self::C::S> self = this;
|
||||
}
|
||||
method baz() → dynamic {
|
||||
return new self::Closure#C#baz#function::•<self::C::T, self::C::S>(null);
|
||||
}
|
||||
static factory •<T extends core::Object, S extends core::Object>() → self::C<self::C::•::T, self::C::•::S> {
|
||||
final dynamic local = new self::Closure#C#function#local::•<self::C::•::T, self::C::•::S>(null);
|
||||
return local.call();
|
||||
}
|
||||
}
|
||||
class Closure#C#foo#function<T extends core::Object, S extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#foo#function::context = context
|
||||
;
|
||||
method call(self::Closure#C#foo#function::T x) → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#foo#function::context};
|
||||
self::Closure#C#foo#function::T y = x;
|
||||
core::Object z = y;
|
||||
self::C<self::Closure#C#foo#function::T, self::Closure#C#foo#function::S> self = #contextParameter.[](0);
|
||||
return z as self::Closure#C#foo#function::T;
|
||||
}
|
||||
}
|
||||
class Closure#C#baz#function#function<T extends core::Object, S extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#baz#function#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#baz#function#function::context};
|
||||
return self::C::•<self::Closure#C#baz#function#function::T, self::Closure#C#baz#function#function::S>();
|
||||
}
|
||||
}
|
||||
class Closure#C#baz#function<T extends core::Object, S extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#baz#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#baz#function::context};
|
||||
return new self::Closure#C#baz#function#function::•<self::Closure#C#baz#function::T, self::Closure#C#baz#function::S>(#contextParameter);
|
||||
}
|
||||
}
|
||||
class Closure#C#function#local<T extends core::Object, S extends core::Object> extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#C#function#local::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#C#function#local::context};
|
||||
self::C<self::Closure#C#function#local::T, self::Closure#C#function#local::S> self = new self::C::internal<self::Closure#C#function#local::T, self::Closure#C#function#local::S>();
|
||||
return self;
|
||||
}
|
||||
}
|
||||
static method main(dynamic arguments) → dynamic {
|
||||
core::print(self::C::•<core::String, core::String>().foo(null).call(arguments.first));
|
||||
dynamic c = self::C::•<core::int, core::int>().baz().call().call();
|
||||
if(!(c is self::C<core::int, core::int>))
|
||||
throw "${c} fails type test 'is C<int, int>'";
|
||||
if(c is self::C<core::String, core::String>) {
|
||||
throw "${c} passes type test 'is C<String, String>'";
|
||||
}
|
||||
core::print(c);
|
||||
}
|
22
pkg/kernel/testcases/closures/uncaptured_for_in_loop.dart
Normal file
22
pkg/kernel/testcases/closures/uncaptured_for_in_loop.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE.md file.
|
||||
|
||||
const numbers = const <int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
main() {
|
||||
var closures = [];
|
||||
for (int i in numbers) {
|
||||
int j = i;
|
||||
closures.add(() => j);
|
||||
}
|
||||
int sum = 0;
|
||||
for (Function f in closures) {
|
||||
sum += f();
|
||||
}
|
||||
// This formula is credited to Gauss. Search for "Gauss adding 1 to 100".
|
||||
int expectedSum = (numbers.length - 1) * numbers.length ~/ 2;
|
||||
if (expectedSum != sum) {
|
||||
throw new Exception("Unexpected sum = $sum != $expectedSum");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
library;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
import "dart:mock" as mock;
|
||||
|
||||
class Closure#main#function extends core::Object implements core::Function {
|
||||
field core::String note = "This is temporary. The VM doesn't need closure classes.";
|
||||
field mock::Context context;
|
||||
constructor •(final mock::Context context) → dynamic
|
||||
: self::Closure#main#function::context = context
|
||||
;
|
||||
method call() → dynamic {
|
||||
"This is a temporary solution. In the VM, this will become an additional parameter.";
|
||||
final mock::Context #contextParameter = this.{self::Closure#main#function::context};
|
||||
return #contextParameter.[](0);
|
||||
}
|
||||
}
|
||||
static const field dynamic numbers = const <core::int>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
static method main() → dynamic {
|
||||
dynamic closures = <dynamic>[];
|
||||
for (core::int i in self::numbers) {
|
||||
final mock::Context #context = new mock::Context::•(1);
|
||||
#context.parent = null;
|
||||
#context.[]=(0, i);
|
||||
closures.add(new self::Closure#main#function::•(#context));
|
||||
}
|
||||
core::int sum = 0;
|
||||
for (core::Function f in closures) {
|
||||
sum = sum.+(f.call());
|
||||
}
|
||||
core::int expectedSum = self::numbers.length.-(1).*(self::numbers.length).~/(2);
|
||||
if(!expectedSum.==(sum)) {
|
||||
throw core::Exception::•("Unexpected sum = ${sum} != ${expectedSum}");
|
||||
}
|
||||
}
|
|
@ -19,6 +19,12 @@ dart_messages/test/dart_messages_test: Skip # Requires a package root.
|
|||
# Skip dev_compiler codegen tests
|
||||
dev_compiler/test/codegen/*: Skip
|
||||
|
||||
[ $runtime == vm && $mode == release && $system == linux ]
|
||||
kernel/test/closures_test: Slow, Pass
|
||||
|
||||
[ $runtime != vm || $mode != release || $system != linux ]
|
||||
kernel/test/closures_test: Skip
|
||||
|
||||
# Analyze dev_compiler but don't run its tests
|
||||
[ $compiler != dart2analyzer ]
|
||||
dev_compiler/test/*: Skip
|
||||
|
|
Loading…
Reference in a new issue