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:
Karl Klose 2016-12-15 10:16:22 +01:00
parent f7b6f0cf1c
commit ff99a0ce59
66 changed files with 3400 additions and 66 deletions

View file

@ -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
View file

@ -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"),

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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:

View file

@ -365,6 +365,7 @@ class ClassHierarchy {
inherited = _getUnshadowedInheritedMembers(declared, inherited);
allInheritedMembers = _merge(allInheritedMembers, inherited);
}
inheritFrom(classNode.supertype);
inheritFrom(classNode.mixedInType);
classNode.implementedTypes.forEach(inheritFrom);

View file

@ -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,

View file

@ -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

View file

@ -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) {

View file

@ -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);
}
}

View 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 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;
}

View 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);
}
}

View 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));
}
}

View 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);
}
}

View 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;
}

View 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);
}

View file

@ -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);

View file

@ -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.
///

View file

@ -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);

View file

@ -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}

View file

@ -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);

View file

@ -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');

View file

@ -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)]);
}

View 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");
}

View 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");

View 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": [
]
}
}

View 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)));
}

View file

@ -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")})';

View file

@ -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

View file

@ -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) {

View file

@ -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) {

View file

@ -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 {

View file

@ -58,7 +58,6 @@ bias.right: ${formatTime(rightBiasTime)}
''');
}
String formatTime(int microseconds) {
double seconds = microseconds / 1000000.0;
return '$seconds s';

View 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

View 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();
}

View 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();
}

View 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])();
}

View file

@ -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();
}

View 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";
}

View 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}";
}

View 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");
}

View 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");
}

View 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();
}

View 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();
}

View 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";
}

View 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";
}

View 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");
}
}

View 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}");
}
}

View 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");
}
}

View 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}");
}
}

View 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.";
}
}

View 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};
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}.";
}
}

View 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>());
}

View 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>());
}

View 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();
}

View 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();
}

View 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";
}
}

View 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)}";
}
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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");
}
}

View file

@ -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}");
}
}

View file

@ -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