Refactor output code for generators

sync*/async/async* generators have two parts: an entry, which does
checks and computes the element type of the result, and a body, which
is transformed after lowering to JavaScript.

The split ensures that the code for checks is done at function call
time, rather than e.g. in the moveNext method of a sync* iterator.

A following CL with optimize the split to generate one function when
there are no checks and the type argument is simple enough for textual
substitution.

Change-Id: I5414109ca851f9267871aa113a2e29b16236986d
Reviewed-on: https://dart-review.googlesource.com/54308
Commit-Queue: Stephen Adams <sra@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Stephen Adams 2018-05-09 18:22:07 +00:00 committed by commit-bot@chromium.org
parent e67222a92b
commit 6efd3b3654
27 changed files with 413 additions and 78 deletions

View file

@ -1393,6 +1393,9 @@ abstract class ElementEnvironment {
/// Calls [f] for each class member declared in [cls].
void forEachLocalClassMember(ClassEntity cls, void f(MemberEntity member));
/// Calls [f] for each class member added to [cls] during compilation.
void forEachInjectedClassMember(ClassEntity cls, void f(MemberEntity member));
/// Calls [f] for each class member declared or inherited in [cls] together
/// with the class that declared the member.
///

View file

@ -212,8 +212,8 @@ class KernelInferrerEngine extends InferrerEngineImpl<ir.Node> {
}
break;
case MemberKind.closureField:
break;
case MemberKind.signature:
case MemberKind.generatorBody:
break;
}
failedAt(member, 'Unexpected member definition: $definition.');

View file

@ -144,6 +144,7 @@ class KernelSourceInformationBuilder
return _buildFunction(name, base ?? node, node.function);
}
break;
// TODO(sra): generatorBody
default:
}
return _buildTreeNode(base ?? node, name: name);
@ -209,6 +210,16 @@ class KernelSourceInformationBuilder
return _buildBody(node, node.function.body);
}
break;
case MemberKind.generatorBody:
ir.Node node = definition.node;
if (node is ir.FunctionDeclaration) {
return _buildBody(node, node.function.body);
} else if (node is ir.FunctionExpression) {
return _buildBody(node, node.function.body);
} else if (node is ir.Member && node.function != null) {
return _buildBody(node, node.function.body);
}
break;
default:
}
return _buildTreeNode(definition.node);

View file

@ -159,6 +159,9 @@ abstract class AsyncRewriterBase extends js.NodeVisitor {
js.VariableUse get self => new js.VariableUse(selfName);
String selfName;
/// The rewritten code can take type arguments. These are added if needed.
List<String> typeArgumentNames = <String>[];
final DiagnosticReporter reporter;
// For error reporting only.
Spannable get spannable {
@ -245,6 +248,15 @@ abstract class AsyncRewriterBase extends js.NodeVisitor {
return result;
}
List<js.Expression> processTypeArguments(List<js.Expression> types) {
if (types == null) {
String name = freshName('type');
typeArgumentNames.add(name);
return <js.Expression>[new js.VariableUse(name)];
}
return types;
}
/// All the pieces are collected in this map, to create a switch with a case
/// for each label.
///
@ -532,6 +544,7 @@ abstract class AsyncRewriterBase extends js.NodeVisitor {
/// Returns the rewritten function.
js.Fun finishFunction(
List<js.Parameter> parameters,
List<js.Parameter> typeParameters,
js.Statement rewrittenBody,
js.VariableDeclarationList variableDeclarations,
SourceInformation functionSourceInformation,
@ -738,8 +751,11 @@ abstract class AsyncRewriterBase extends js.NodeVisitor {
js.VariableDeclarationList variableDeclarations =
new js.VariableDeclarationList(variables);
return finishFunction(node.params, rewrittenBody, variableDeclarations,
node.sourceInformation, bodySourceInformation);
// Names are already safe when added.
List<js.Parameter> typeParameters =
typeArgumentNames.map((name) => new js.Parameter(name)).toList();
return finishFunction(node.params, typeParameters, rewrittenBody,
variableDeclarations, node.sourceInformation, bodySourceInformation);
}
@override
@ -1739,7 +1755,7 @@ class AsyncRewriter extends AsyncRewriterBase {
///
/// Specific to async methods.
final js.Expression completerFactory;
final js.Expression completerFactoryTypeArgument;
List<js.Expression> completerFactoryTypeArguments;
final js.Expression wrapBody;
@ -1749,7 +1765,7 @@ class AsyncRewriter extends AsyncRewriterBase {
this.asyncReturn,
this.asyncRethrow,
this.completerFactory,
this.completerFactoryTypeArgument,
this.completerFactoryTypeArguments,
this.wrapBody,
String safeVariableName(String proposedName),
js.Name bodyName})
@ -1803,7 +1819,7 @@ class AsyncRewriter extends AsyncRewriterBase {
completer,
js.js('#(#)', [
completerFactory,
completerFactoryTypeArgument
completerFactoryTypeArguments
]).withSourceInformation(sourceInformation),
sourceInformation));
if (analysis.hasExplicitReturns) {
@ -1816,6 +1832,8 @@ class AsyncRewriter extends AsyncRewriterBase {
@override
void initializeNames() {
completerName = freshName("completer");
completerFactoryTypeArguments =
processTypeArguments(completerFactoryTypeArguments);
}
@override
@ -1833,6 +1851,7 @@ class AsyncRewriter extends AsyncRewriterBase {
@override
js.Fun finishFunction(
List<js.Parameter> parameters,
List<js.Parameter> typeParameters,
js.Statement rewrittenBody,
js.VariableDeclarationList variableDeclarations,
SourceInformation functionSourceInformation,
@ -1884,12 +1903,13 @@ class AsyncRewriter extends AsyncRewriterBase {
"innerFunction": innerFunction,
}).withSourceInformation(bodySourceInformation);
return js.js("""
function (#parameters) {
function (#parameters, #typeParameters) {
#variableDeclarations;
var #bodyName = #wrapBodyCall;
#returnAsyncStart;
}""", {
"parameters": parameters,
"typeParameters": typeParameters,
"variableDeclarations": variableDeclarations,
"bodyName": bodyName,
"wrapBodyCall": wrapBodyCall,
@ -1904,7 +1924,7 @@ class SyncStarRewriter extends AsyncRewriterBase {
/// Constructor creating the Iterable for a sync* method. Called with
/// [bodyName].
final js.Expression iterableFactory;
final js.Expression iterableFactoryTypeArgument;
List<js.Expression> iterableFactoryTypeArguments;
/// A JS Expression that creates a marker showing that iteration is over.
///
@ -1922,7 +1942,7 @@ class SyncStarRewriter extends AsyncRewriterBase {
SyncStarRewriter(DiagnosticReporter diagnosticListener, spannable,
{this.endOfIteration,
this.iterableFactory,
this.iterableFactoryTypeArgument,
this.iterableFactoryTypeArguments,
this.yieldStarExpression,
this.uncaughtErrorExpression,
String safeVariableName(String proposedName),
@ -1949,6 +1969,7 @@ class SyncStarRewriter extends AsyncRewriterBase {
@override
js.Fun finishFunction(
List<js.Parameter> parameters,
List<js.Parameter> typeParameters,
js.Statement rewrittenBody,
js.VariableDeclarationList variableDeclarations,
SourceInformation functionSourceInformation,
@ -2017,19 +2038,20 @@ class SyncStarRewriter extends AsyncRewriterBase {
js.Expression callIterableFactory =
js.js("#iterableFactory(#innerFunction, #type)", {
"iterableFactory": iterableFactory,
"type": iterableFactoryTypeArgument,
"type": iterableFactoryTypeArguments,
"innerFunction": innerFunction,
}).withSourceInformation(bodySourceInformation);
js.Statement returnCallIterableFactory = new js.Return(callIterableFactory)
.withSourceInformation(bodySourceInformation);
return js.js("""
function (#renamedParameters) {
function (#renamedParameters, #typeParameters) {
if (#needsThis)
var #self = this;
#returnCallIterableFactory;
}
""", {
"renamedParameters": renamedParameters,
"typeParameters": typeParameters,
"needsThis": analysis.hasThis,
"self": selfName,
"returnCallIterableFactory": returnCallIterableFactory,
@ -2075,7 +2097,10 @@ class SyncStarRewriter extends AsyncRewriterBase {
}
@override
void initializeNames() {}
void initializeNames() {
iterableFactoryTypeArguments =
processTypeArguments(iterableFactoryTypeArguments);
}
}
class AsyncStarRewriter extends AsyncRewriterBase {
@ -2115,7 +2140,7 @@ class AsyncStarRewriter extends AsyncRewriterBase {
///
/// Specific to async* methods.
final js.Expression newController;
final js.Expression newControllerTypeArgument;
List<js.Expression> newControllerTypeArguments;
/// Used to get the `Stream` out of the [controllerName] variable.
final js.Expression streamOfController;
@ -2136,7 +2161,7 @@ class AsyncStarRewriter extends AsyncRewriterBase {
{this.asyncStarHelper,
this.streamOfController,
this.newController,
this.newControllerTypeArgument,
this.newControllerTypeArguments,
this.yieldExpression,
this.yieldStarExpression,
this.wrapBody,
@ -2184,6 +2209,7 @@ class AsyncStarRewriter extends AsyncRewriterBase {
@override
js.Fun finishFunction(
List<js.Parameter> parameters,
List<js.Parameter> typeParameters,
js.Statement rewrittenBody,
js.VariableDeclarationList variableDeclarations,
SourceInformation functionSourceInformation,
@ -2277,12 +2303,13 @@ class AsyncStarRewriter extends AsyncRewriterBase {
new js.Return(streamOfControllerCall)
.withSourceInformation(bodySourceInformation);
return js.js("""
function (#parameters) {
function (#parameters, #typeParameters) {
#declareBodyName;
#variableDeclarations;
#returnStreamOfControllerCall;
}""", {
"parameters": parameters,
"typeParameters": typeParameters,
"declareBodyName": declareBodyName,
"variableDeclarations": variableDeclarations,
"returnStreamOfControllerCall": returnStreamOfControllerCall,
@ -2329,7 +2356,7 @@ class AsyncStarRewriter extends AsyncRewriterBase {
js.js('#(#, #)', [
newController,
bodyName,
newControllerTypeArgument
newControllerTypeArguments
]).withSourceInformation(sourceInformation),
sourceInformation));
if (analysis.hasYield) {
@ -2343,6 +2370,8 @@ class AsyncStarRewriter extends AsyncRewriterBase {
void initializeNames() {
controllerName = freshName("controller");
nextWhenCanceledName = freshName("nextWhenCanceled");
newControllerTypeArguments =
processTypeArguments(newControllerTypeArguments);
}
@override

View file

@ -1165,7 +1165,8 @@ class JavaScriptBackend {
if (element.asyncMarker == AsyncMarker.SYNC) return code;
AsyncRewriterBase rewriter = null;
jsAst.Name name = namer.methodPropertyName(element);
jsAst.Name name = namer.methodPropertyName(
element is JGeneratorBody ? element.function : element);
switch (element.asyncMarker) {
case AsyncMarker.ASYNC:
@ -1178,7 +1179,7 @@ class JavaScriptBackend {
emitter.staticFunctionAccess(commonElements.endOfIteration),
iterableFactory: emitter
.staticFunctionAccess(commonElements.syncStarIterableFactory),
iterableFactoryTypeArgument:
iterableFactoryTypeArguments:
_fetchItemType(element, elementEnvironment),
yieldStarExpression:
emitter.staticFunctionAccess(commonElements.yieldStar),
@ -1201,7 +1202,7 @@ class JavaScriptBackend {
wrapBody: emitter.staticFunctionAccess(commonElements.wrapBody),
newController: emitter.staticFunctionAccess(
commonElements.asyncStarStreamControllerFactory),
newControllerTypeArgument:
newControllerTypeArguments:
_fetchItemType(element, elementEnvironment),
safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
yieldExpression:
@ -1219,19 +1220,22 @@ class JavaScriptBackend {
return rewriter.rewrite(code, bodySourceInformation, exitSourceInformation);
}
/// Returns an expression that evaluates the type argument to the
/// Returns an optional expression that evaluates the type argument to the
/// Future/Stream/Iterable.
jsAst.Expression _fetchItemType(
/// Returns an empty list if the type is not needed.
/// Returns `null` if the type expression is determined by
/// the outside context and should be added as a function parameter.
List<jsAst.Expression> _fetchItemType(
FunctionEntity element, ElementEnvironment elementEnvironment) {
DartType type =
elementEnvironment.getFunctionAsyncOrSyncStarElementType(element);
//DartType type =
// elementEnvironment.getFunctionAsyncOrSyncStarElementType(element);
if (!type.containsFreeTypeVariables) {
return rtiEncoder.getTypeRepresentation(emitter.emitter, type, null);
}
//if (!type.containsFreeTypeVariables) {
// var ast = rtiEncoder.getTypeRepresentation(emitter.emitter, type, null);
// return <jsAst.Expression>[ast];
//}
// TODO(sra): Handle types that have type variables.
return js('null');
return null;
}
AsyncRewriter _makeAsyncRewriter(
@ -1250,7 +1254,7 @@ class JavaScriptBackend {
? commonElements.asyncAwaitCompleterFactory
: commonElements.syncCompleterFactory;
jsAst.Expression itemTypeExpression =
List<jsAst.Expression> itemTypeExpression =
_fetchItemType(element, elementEnvironment);
var rewriter = new AsyncRewriter(reporter, element,
@ -1263,7 +1267,7 @@ class JavaScriptBackend {
emitter.staticFunctionAccess(commonElements.asyncHelperRethrow),
wrapBody: emitter.staticFunctionAccess(commonElements.wrapBody),
completerFactory: emitter.staticFunctionAccess(completerFactory),
completerFactoryTypeArgument: itemTypeExpression,
completerFactoryTypeArguments: itemTypeExpression,
safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
bodyName: namer.deriveAsyncBodyName(name));

View file

@ -24,6 +24,7 @@ import '../elements/resolution_types.dart';
import '../elements/types.dart';
import '../js/js.dart' as jsAst;
import '../js_model/closure.dart';
import '../js_model/elements.dart' show JGeneratorBody;
import '../universe/call_structure.dart' show CallStructure;
import '../universe/selector.dart' show Selector, SelectorKind;
import '../universe/world_builder.dart' show CodegenWorldBuilder;
@ -782,13 +783,29 @@ class Namer {
ctor, () => _proposeNameForConstructorBody(ctor));
}
/// Name for a generator body.
jsAst.Name generatorBodyInstanceMethodName(JGeneratorBody method) {
assert(method.isInstanceMember);
// TODO(sra): Except for methods declared in mixins, we can use a compact
// naming scheme like we do for [ConstructorBodyEntity].
FunctionEntity function = method.function;
return _disambiguateInternalMember(method, () {
String invocationName = operatorNameToIdentifier(function.name);
return '${invocationName}\$body\$${method.enclosingClass.name}';
});
}
/// Annotated name for [method] encoding arity and named parameters.
jsAst.Name instanceMethodName(FunctionEntity method) {
// TODO(johnniwinther): Avoid the use of [ConstructorBodyEntity]. The
// codegen model should be explicit about its constructor body elements.
// TODO(johnniwinther): Avoid the use of [ConstructorBodyEntity] and
// [JGeneratorBody]. The codegen model should be explicit about its
// constructor body elements.
if (method is ConstructorBodyEntity) {
return constructorBodyName(method);
}
if (method is JGeneratorBody) {
return generatorBodyInstanceMethodName(method);
}
return invocationName(new Selector.fromElement(method));
}
@ -1337,6 +1354,8 @@ class Namer {
String _proposeNameForMember(MemberEntity element) {
if (element.isConstructor) {
return _proposeNameForConstructor(element);
} else if (element is JGeneratorBody) {
return _proposeNameForMember(element.function) + r'$body';
} else if (element.enclosingClass != null) {
ClassEntity enclosingClass = element.enclosingClass;
return '${enclosingClass.name}_${element.name}';

View file

@ -743,6 +743,7 @@ class ProgramBuilder {
if (!onlyForRti && !_elementEnvironment.isMixinApplication(cls)) {
List<MemberEntity> members = <MemberEntity>[];
_elementEnvironment.forEachLocalClassMember(cls, members.add);
_elementEnvironment.forEachInjectedClassMember(cls, members.add);
_elementEnvironment.forEachConstructorBody(cls, members.add);
_sorter.sortMembers(members).forEach(visitMember);
}

View file

@ -143,6 +143,10 @@ class JsElementCreatorMixin {
return new JConstructorBody(constructor);
}
JGeneratorBody createGeneratorBody(FunctionEntity function) {
return new JGeneratorBody(function);
}
IndexedFunction createGetter(LibraryEntity library,
ClassEntity enclosingClass, Name name, AsyncMarker asyncMarker,
{bool isStatic, bool isExternal, bool isAbstract}) {
@ -498,6 +502,17 @@ class JMethod extends JFunction {
String get _kind => 'method';
}
class JGeneratorBody extends JFunction {
final FunctionEntity function;
JGeneratorBody(this.function)
: super(function.library, function.enclosingClass, function.memberName,
function.parameterStructure, function.asyncMarker,
isStatic: function.isStatic, isExternal: false);
String get _kind => 'generator_body';
}
class JGetter extends JFunction {
final bool isAbstract;

View file

@ -14,6 +14,8 @@ import '../elements/types.dart';
import '../kernel/element_map.dart';
import '../kernel/indexed.dart';
import '../js_model/elements.dart' show JGeneratorBody;
class GlobalLocalsMap {
Map<MemberEntity, KernelToLocalsMap> _localsMaps =
<MemberEntity, KernelToLocalsMap>{};
@ -419,7 +421,9 @@ class JLocal extends IndexedLocal {
/// True if this local represents a local parameter.
final bool isRegularParameter;
JLocal(this.name, this.memberContext, {this.isRegularParameter: false});
JLocal(this.name, this.memberContext, {this.isRegularParameter: false}) {
assert(memberContext is! JGeneratorBody);
}
String get _kind => 'local';

View file

@ -17,6 +17,7 @@ import '../js_backend/namer.dart';
import '../js_backend/native_data.dart';
import '../js_emitter/code_emitter_task.dart';
import '../js_model/closure.dart' show JRecordField, KernelScopeInfo;
import '../js_model/elements.dart' show JGeneratorBody;
import '../native/native.dart' as native;
import '../ssa/type_builder.dart';
import '../types/types.dart';
@ -236,6 +237,9 @@ abstract class KernelToElementMapForBuilding implements KernelToElementMap {
/// Returns the constructor body entity corresponding to [constructor].
FunctionEntity getConstructorBody(ir.Constructor node);
/// Returns the constructor body entity corresponding to [function].
JGeneratorBody getGeneratorBody(FunctionEntity function);
/// Make a record to ensure variables that are are declared in one scope and
/// modified in another get their values updated correctly.
Map<Local, JRecordField> makeRecordContainer(
@ -261,6 +265,8 @@ enum MemberKind {
// the closure class. It does not have a corresponding ir.Node or a method
// body.
signature,
// A separated body of a generator (sync*/async/async*) function.
generatorBody,
}
/// Definition information for a [MemberEntity].

View file

@ -748,6 +748,13 @@ abstract class KernelToElementMapBase extends KernelToElementMapBaseMixin {
});
}
void _forEachInjectedClassMember(
IndexedClass cls, void f(MemberEntity member)) {
assert(checkFamily(cls));
throw new UnsupportedError(
'KernelToElementMapBase._forEachInjectedClassMember');
}
void _forEachClassMember(
IndexedClass cls, void f(ClassEntity cls, MemberEntity member)) {
assert(checkFamily(cls));
@ -1373,7 +1380,7 @@ class KernelToElementMapForImpactImpl extends KernelToElementMapBase
/// Returns the element type of a async/sync*/async* function.
@override
DartType getFunctionAsyncOrSyncStarElementType(ir.FunctionNode functionNode) {
DartType returnType = getFunctionType(functionNode).returnType;
DartType returnType = getDartType(functionNode.returnType);
switch (functionNode.asyncMarker) {
case ir.AsyncMarker.SyncStar:
return elementEnvironment.getAsyncOrSyncStarElementType(
@ -1471,6 +1478,7 @@ class KernelElementEnvironment extends ElementEnvironment {
@override
DartType getFunctionAsyncOrSyncStarElementType(FunctionEntity function) {
// TODO(sra): Should be getting the DartType from the node.
DartType returnType = getFunctionType(function).returnType;
return getAsyncOrSyncStarElementType(function.asyncMarker, returnType);
}
@ -1580,6 +1588,12 @@ class KernelElementEnvironment extends ElementEnvironment {
elementMap._forEachLocalClassMember(cls, f);
}
@override
void forEachInjectedClassMember(
ClassEntity cls, void f(MemberEntity member)) {
elementMap._forEachInjectedClassMember(cls, f);
}
@override
void forEachClassMember(
ClassEntity cls, void f(ClassEntity declarer, MemberEntity member)) {
@ -2257,6 +2271,12 @@ class JsKernelToElementMap extends KernelToElementMapBase
NativeBasicData nativeBasicData;
Map<FunctionEntity, JGeneratorBody> _generatorBodies =
<FunctionEntity, JGeneratorBody>{};
Map<ClassEntity, List<MemberEntity>> _injectedClassMembers =
<ClassEntity, List<MemberEntity>>{};
JsKernelToElementMap(
DiagnosticReporter reporter,
Environment environment,
@ -2514,6 +2534,11 @@ class JsKernelToElementMap extends KernelToElementMapBase
env.forEachConstructorBody(f);
}
void _forEachInjectedClassMember(
IndexedClass cls, void f(MemberEntity member)) {
_injectedClassMembers[cls]?.forEach(f);
}
JRecordField _constructRecordFieldEntry(
InterfaceType memberThisType,
ir.VariableDeclaration variable,
@ -2911,6 +2936,31 @@ class JsKernelToElementMap extends KernelToElementMapBase
String _getClosureVariableName(String name, int id) {
return "_captured_${name}_$id";
}
JGeneratorBody getGeneratorBody(covariant IndexedFunction function) {
FunctionData functionData = _members.getData(function);
ir.TreeNode node = functionData.definition.node;
// TODO(sra): Maybe store this in the FunctionData.
JGeneratorBody generatorBody = _generatorBodies[function];
if (generatorBody == null) {
generatorBody = createGeneratorBody(function);
_members.register<IndexedFunction, FunctionData>(
generatorBody,
new GeneratorBodyFunctionData(
functionData,
new SpecialMemberDefinition(
generatorBody, node, MemberKind.generatorBody)));
if (function.enclosingClass != null) {
// TODO(sra): Integrate this with ClassEnvImpl.addConstructorBody ?
(_injectedClassMembers[function.enclosingClass] ??= <MemberEntity>[])
.add(generatorBody);
}
}
return generatorBody;
}
JGeneratorBody createGeneratorBody(FunctionEntity function);
}
class KernelClassQueries extends ClassQueries {

View file

@ -829,6 +829,44 @@ class SignatureFunctionData implements FunctionData {
}
}
abstract class DelegatedFunctionData implements FunctionData {
final FunctionData baseData;
DelegatedFunctionData(this.baseData);
FunctionType getFunctionType(covariant KernelToElementMapBase elementMap) {
return baseData.getFunctionType(elementMap);
}
List<TypeVariableType> getFunctionTypeVariables(
KernelToElementMap elementMap) {
return baseData.getFunctionTypeVariables(elementMap);
}
void forEachParameter(KernelToElementMapForBuilding elementMap,
void f(DartType type, String name, ConstantValue defaultValue)) {
return baseData.forEachParameter(elementMap, f);
}
@override
Iterable<ConstantValue> getMetadata(KernelToElementMap elementMap) {
return const <ConstantValue>[];
}
InterfaceType getMemberThisType(KernelToElementMapForBuilding elementMap) {
return baseData.getMemberThisType(elementMap);
}
ClassTypeVariableAccess get classTypeVariableAccess =>
baseData.classTypeVariableAccess;
}
class GeneratorBodyFunctionData extends DelegatedFunctionData {
final MemberDefinition definition;
GeneratorBodyFunctionData(FunctionData baseData, this.definition)
: super(baseData);
}
abstract class ConstructorData extends FunctionData {
ConstantConstructor getConstructorConstant(
KernelToElementMapBase elementMap, ConstructorEntity constructor);

View file

@ -551,6 +551,10 @@ class _CompilerElementEnvironment extends ElementEnvironment {
}
}
@override
void forEachInjectedClassMember(
covariant ClassElement cls, void f(MemberElement member)) {}
@override
void forEachClassMember(covariant ClassElement cls,
void f(ClassElement declarer, MemberElement member)) {

View file

@ -30,6 +30,7 @@ import '../js_backend/runtime_types.dart' show RuntimeTypesSubstitutions;
import '../js_emitter/js_emitter.dart' show NativeEmitter;
import '../js_model/locals.dart'
show forEachOrderedParameter, GlobalLocalsMap, JumpVisitor;
import '../js_model/elements.dart' show JGeneratorBody;
import '../kernel/element_map.dart';
import '../kernel/kernel_backend_strategy.dart';
import '../native/native.dart' as native;
@ -70,6 +71,7 @@ class StackFrame {
class KernelSsaGraphBuilder extends ir.Visitor
with GraphBuilder, SsaBuilderFieldMixin {
final MemberEntity targetElement;
final MemberEntity initialTargetElement;
final ClosedWorld closedWorld;
final CodegenWorldBuilder _worldBuilder;
@ -124,7 +126,7 @@ class KernelSsaGraphBuilder extends ir.Visitor
StackFrame _currentFrame;
KernelSsaGraphBuilder(
this.targetElement,
this.initialTargetElement,
InterfaceType instanceType,
this.compiler,
this._elementMap,
@ -136,7 +138,8 @@ class KernelSsaGraphBuilder extends ir.Visitor
this.closureDataLookup,
this.nativeEmitter,
this._sourceInformationStrategy)
: _infoReporter = compiler.dumpInfoTask {
: this.targetElement = _effectiveTargetElementFor(initialTargetElement),
_infoReporter = compiler.dumpInfoTask {
_enterFrame(targetElement);
this.loopHandler = new KernelLoopHandler(this);
typeBuilder = new KernelTypeBuilder(this, _elementMap, _globalLocalsMap);
@ -157,6 +160,11 @@ class KernelSsaGraphBuilder extends ir.Visitor
SourceInformationBuilder get _sourceInformationBuilder =>
_currentFrame.sourceInformationBuilder;
static MemberEntity _effectiveTargetElementFor(MemberEntity member) {
if (member is JGeneratorBody) return member.function;
return member;
}
void _enterFrame(MemberEntity member) {
AsyncMarker asyncMarker = AsyncMarker.SYNC;
ir.FunctionNode function = getFunctionNode(_elementMap, member);
@ -183,7 +191,7 @@ class KernelSsaGraphBuilder extends ir.Visitor
// TODO(het): no reason to do this here...
HInstruction.idCounter = 0;
MemberDefinition definition =
_elementMap.getMemberDefinition(targetElement);
_elementMap.getMemberDefinition(initialTargetElement);
switch (definition.kind) {
case MemberKind.regular:
@ -247,6 +255,10 @@ class KernelSsaGraphBuilder extends ir.Visitor
}
buildMethodSignature(originalClosureNode);
break;
case MemberKind.generatorBody:
buildGeneratorBody(
initialTargetElement, _functionNodeOf(definition.node));
break;
}
assert(graph.isValid());
@ -269,6 +281,13 @@ class KernelSsaGraphBuilder extends ir.Visitor
});
}
ir.FunctionNode _functionNodeOf(ir.TreeNode node) {
if (node is ir.Member) return node.function;
if (node is ir.FunctionDeclaration) return node.function;
if (node is ir.FunctionExpression) return node.function;
return null;
}
ir.FunctionNode _ensureDefaultArgumentValues(ir.FunctionNode function) {
// Register all [function]'s default argument values.
//
@ -386,8 +405,8 @@ class KernelSsaGraphBuilder extends ir.Visitor
// Unused, so bind to `dynamic`.
param = graph.addConstantNull(closedWorld);
}
localsHandler.directLocals[
localsHandler.getTypeVariableAsLocal(typeVariableType)] = param;
Local local = localsHandler.getTypeVariableAsLocal(typeVariableType);
localsHandler.directLocals[local] = param;
});
}
@ -414,11 +433,14 @@ class KernelSsaGraphBuilder extends ir.Visitor
// Unused, so bind to `dynamic`.
param = graph.addConstantNull(closedWorld);
}
localsHandler.directLocals[
localsHandler.getTypeVariableAsLocal(typeVariableType)] = param;
Local local = localsHandler.getTypeVariableAsLocal(typeVariableType);
localsHandler.directLocals[local] = param;
functionTypeParameterLocals.add(local);
});
}
List<Local> functionTypeParameterLocals = <Local>[];
/// Builds a generative constructor.
///
/// Generative constructors are built in stages, in effect inlining the
@ -622,7 +644,6 @@ class KernelSsaGraphBuilder extends ir.Visitor
void _invokeConstructorBody(ir.Constructor constructor,
List<HInstruction> inputs, SourceInformation sourceInformation) {
// TODO(sra): Inline the constructor body.
MemberEntity constructorBody = _elementMap.getConstructorBody(constructor);
HInvokeConstructorBody invoke = new HInvokeConstructorBody(
constructorBody, inputs, commonMasks.nonNullType, sourceInformation);
@ -941,6 +962,11 @@ class KernelSsaGraphBuilder extends ir.Visitor
/// Procedures.
void buildFunctionNode(
FunctionEntity function, ir.FunctionNode functionNode) {
if (functionNode.asyncMarker != ir.AsyncMarker.Sync) {
buildGenerator(function, functionNode);
return;
}
openFunction(function, functionNode);
// If [functionNode] is `operator==` we explicitly add a null check at the
@ -968,6 +994,76 @@ class KernelSsaGraphBuilder extends ir.Visitor
closeFunction();
}
/// Builds a SSA graph for a sync*/async/async* generator.
void buildGenerator(FunctionEntity function, ir.FunctionNode functionNode) {
// TODO(sra): Optimize by generating a merged entry + body when (1) there
// are no checks in the entry and (2) the element type is simple.
if (true == true) {
buildGeneratorEntry(function, functionNode);
} else {
openFunction(function, functionNode);
functionNode.body.accept(this);
closeFunction();
}
}
/// Builds a SSA graph for a sync*/async/async* generator body.
void buildGeneratorEntry(
FunctionEntity function, ir.FunctionNode functionNode) {
graph.isGeneratorEntry = true;
// TODO(sra): Omit entry checks.
openFunction(function, functionNode);
// Generate type argument for generator class.
// Tail-call body.
JGeneratorBody body = _elementMap.getGeneratorBody(function);
// Is 'buildAsyncBody' the best location for the entry?
var sourceInformation = _sourceInformationBuilder.buildAsyncBody();
// Forward all the parameters.
List<HInstruction> inputs = <HInstruction>[];
if (graph.thisInstruction != null) {
inputs.add(graph.thisInstruction);
}
if (graph.explicitReceiverParameter != null) {
inputs.add(graph.explicitReceiverParameter);
}
for (Local local in parameters.keys) {
inputs.add(localsHandler.readLocal(local));
}
for (Local local in functionTypeParameterLocals) {
inputs.add(localsHandler.readLocal(local));
}
// Add the type parameter for the generator's element type.
DartType elementType = _elementMap.elementEnvironment
.getAsyncOrSyncStarElementType(function.asyncMarker, _returnType);
inputs.add(typeBuilder.analyzeTypeArgument(elementType, function));
push(new HInvokeGeneratorBody(
body,
inputs,
commonMasks.dynamicType, // TODO: better type.
sourceInformation));
closeAndGotoExit(
new HReturn(abstractValueDomain, pop(), sourceInformation));
closeFunction();
}
/// Builds a SSA graph for a sync*/async/async* generator body.
void buildGeneratorBody(
JGeneratorBody function, ir.FunctionNode functionNode) {
// TODO(sra): Omit entry checks.
FunctionEntity entry = function.function;
openFunction(entry, functionNode);
functionNode.body.accept(this);
closeFunction();
}
void _potentiallyAddFunctionParameterTypeChecks(ir.FunctionNode function) {
// Put the type checks in the first successor of the entry,
// because that is where the type guards will also be inserted.

View file

@ -23,6 +23,7 @@ import '../js_backend/native_data.dart';
import '../js_backend/namer.dart';
import '../js_backend/runtime_types.dart';
import '../js_emitter/code_emitter_task.dart';
import '../js_model/elements.dart' show JGeneratorBody;
import '../native/native.dart' as native;
import '../options.dart';
import '../types/abstract_value_domain.dart';
@ -47,20 +48,24 @@ class SsaCodeGeneratorTask extends CompilerTask {
String get name => 'SSA code generator';
js.Fun buildJavaScriptFunction(
FunctionEntity element, List<js.Parameter> parameters, js.Block body) {
js.AsyncModifier asyncModifier = element.asyncMarker.isAsync
js.Fun buildJavaScriptFunction(bool isGeneratorEntry, FunctionEntity element,
List<js.Parameter> parameters, js.Block body) {
js.Fun finish(js.AsyncModifier asyncModifier) {
return new js.Fun(parameters, body, asyncModifier: asyncModifier)
.withSourceInformation(sourceInformationFactory
.createBuilderForContext(element)
.buildDeclaration(element));
}
if (isGeneratorEntry) return finish(const js.AsyncModifier.sync());
return finish(element.asyncMarker.isAsync
? (element.asyncMarker.isYielding
? const js.AsyncModifier.asyncStar()
: const js.AsyncModifier.async())
: (element.asyncMarker.isYielding
? const js.AsyncModifier.syncStar()
: const js.AsyncModifier.sync());
return new js.Fun(parameters, body, asyncModifier: asyncModifier)
.withSourceInformation(sourceInformationFactory
.createBuilderForContext(element)
.buildDeclaration(element));
: const js.AsyncModifier.sync()));
}
js.Expression generateCode(
@ -118,8 +123,8 @@ class SsaCodeGeneratorTask extends CompilerTask {
work);
codegen.visitGraph(graph);
backend.tracer.traceGraph("codegen", graph);
return buildJavaScriptFunction(
work.element, codegen.parameters, codegen.body);
return buildJavaScriptFunction(graph.isGeneratorEntry, work.element,
codegen.parameters, codegen.body);
});
}
}
@ -1782,6 +1787,27 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
node.element, new CallStructure.unnamed(arguments.length)));
}
void visitInvokeGeneratorBody(HInvokeGeneratorBody node) {
JGeneratorBody element = node.element;
if (element.isInstanceMember) {
use(node.inputs[0]);
js.Expression object = pop();
List<js.Expression> arguments = visitArguments(node.inputs);
js.Name methodName = _namer.instanceMethodName(element);
push(js
.propertyCall(object, methodName, arguments)
.withSourceInformation(node.sourceInformation));
} else {
push(_emitter.staticFunctionAccess(element));
List<js.Expression> arguments = visitArguments(node.inputs, start: 0);
push(new js.Call(pop(), arguments,
sourceInformation: node.sourceInformation));
}
_registry
.registerStaticUse(new StaticUse.generatorBodyInvoke(node.element));
}
void visitOneShotInterceptor(HOneShotInterceptor node) {
List<js.Expression> arguments = visitArguments(node.inputs);
var isolate = new js.VariableUse(

View file

@ -60,6 +60,7 @@ abstract class HVisitor<R> {
R visitInvokeStatic(HInvokeStatic node);
R visitInvokeSuper(HInvokeSuper node);
R visitInvokeConstructorBody(HInvokeConstructorBody node);
R visitInvokeGeneratorBody(HInvokeGeneratorBody node);
R visitIs(HIs node);
R visitIsViaInterceptor(HIsViaInterceptor node);
R visitLazyStatic(HLazyStatic node);
@ -206,10 +207,15 @@ class HGraph {
HBasicBlock exit;
HThis thisInstruction;
/// `true` if a sync*/async/async* method is split into an entry and a body
/// and this graph is for the entry, which should not be rewritten.
bool isGeneratorEntry = false;
/// Receiver parameter, set for methods using interceptor calling convention.
HParameterValue explicitReceiverParameter;
bool isRecursiveMethod = false;
bool calledInLoop = false;
final List<HBasicBlock> blocks = <HBasicBlock>[];
/// Nodes containing list allocations for which there is a known fixed length.
@ -414,6 +420,8 @@ class HBaseVisitor extends HGraphVisitor implements HVisitor {
visitInvokeClosure(HInvokeClosure node) => visitInvokeDynamic(node);
visitInvokeConstructorBody(HInvokeConstructorBody node) =>
visitInvokeStatic(node);
visitInvokeGeneratorBody(HInvokeGeneratorBody node) =>
visitInvokeStatic(node);
visitInvokeDynamicMethod(HInvokeDynamicMethod node) =>
visitInvokeDynamic(node);
visitInvokeDynamicGetter(HInvokeDynamicGetter node) =>
@ -1821,6 +1829,25 @@ class HInvokeConstructorBody extends HInvokeStatic {
accept(HVisitor visitor) => visitor.visitInvokeConstructorBody(this);
}
class HInvokeGeneratorBody extends HInvokeStatic {
// Directly call the JGeneratorBody method. The generator body can be a static
// method or a member. The target is directly called.
// The 'inputs' are
// [arg1, ..., argN] or
// [receiver, arg1, ..., argN] or
// [interceptor, receiver, arg1, ... argN].
// The 'inputs' may or may not have an additional type argument used for
// creating the generator (T for new Completer<T>() inside the body).
HInvokeGeneratorBody(FunctionEntity element, List<HInstruction> inputs,
AbstractValue type, SourceInformation sourceInformation)
: super(element, inputs, type, const <DartType>[]) {
this.sourceInformation = sourceInformation;
}
String toString() => 'HInvokeGeneratorBody(${element.name})';
accept(HVisitor visitor) => visitor.visitInvokeGeneratorBody(this);
}
abstract class HFieldAccess extends HInstruction {
final FieldEntity element;

View file

@ -45,7 +45,7 @@ class SsaFunctionCompiler implements FunctionCompiler {
optimizer.optimize(work, graph, closedWorld);
MemberEntity element = work.element;
js.Expression result = generator.generateCode(work, graph, closedWorld);
if (element is FunctionEntity) {
if (element is FunctionEntity && !graph.isGeneratorEntry) {
SourceInformationBuilder sourceInformationBuilder =
backend.sourceInformationStrategy.createBuilderForContext(element);
result = backend.rewriteAsync(

View file

@ -350,6 +350,11 @@ class HInstructionStringifier implements HVisitor<String> {
return handleGenericInvoke("InvokeConstructorBody", target, invoke.inputs);
}
String visitInvokeGeneratorBody(HInvokeGeneratorBody invoke) {
String target = invoke.element.name;
return handleGenericInvoke("InvokeGeneratorBody", target, invoke.inputs);
}
String visitForeignCode(HForeignCode node) {
var template = node.codeTemplate;
String code = '${template.ast}';

View file

@ -305,6 +305,12 @@ class StaticUse {
callStructure: callStructure);
}
/// Direct invocation of a generator (body) [element], as a static call or
/// through a this or super constructor call.
factory StaticUse.generatorBodyInvoke(FunctionEntity element) {
return new StaticUse.internal(element, StaticUseKind.INVOKE);
}
/// Direct invocation of a method [element] with the given [callStructure].
factory StaticUse.directInvoke(FunctionEntity element,
CallStructure callStructure, List<DartType> typeArguments) {

View file

@ -31,7 +31,7 @@ void testAsyncTransform(String source, String expected) {
asyncReturn: new VariableUse("returnHelper"),
asyncRethrow: new VariableUse("rethrowHelper"),
completerFactory: new VariableUse("NewCompleter"),
completerFactoryTypeArgument: new VariableUse("CompleterType"),
completerFactoryTypeArguments: [new VariableUse("CompleterType")],
wrapBody: new VariableUse("_wrapJsFunctionForAsync"),
safeVariableName: (String name) => "__$name",
bodyName: new StringBackedName("body")));
@ -44,7 +44,7 @@ void testSyncStarTransform(String source, String expected) {
new SyncStarRewriter(null, null,
endOfIteration: new VariableUse("endOfIteration"),
iterableFactory: new VariableUse("NewIterable"),
iterableFactoryTypeArgument: new VariableUse("IterableType"),
iterableFactoryTypeArguments: [new VariableUse("IterableType")],
yieldStarExpression: new VariableUse("yieldStar"),
uncaughtErrorExpression: new VariableUse("uncaughtError"),
safeVariableName: (String name) => "__$name",

View file

@ -10,6 +10,6 @@ main() {
}
@NoInline()
test() async /*2:test*/ {
/*3:test*/ throw '>ExceptionMarker<';
test() async /*2:test*/ /*kernel.3:test*/ {
/*4:test*/ throw '>ExceptionMarker<';
}

View file

@ -10,12 +10,12 @@ main() {
}
@NoInline()
test1() async /*2:test1*/ {
test1() async /*3:test1*/ /*kernel.4:test1*/ {
// This call is on the stack when the error is thrown.
await /*3:test1*/ test2();
await /*5:test1*/ test2();
}
@NoInline()
test2() async /*4:test2*/ {
/*5:test2*/ throw '>ExceptionMarker<';
test2() async /*7:test2*/ /*kernel.8:test2*/ {
/*9:test2*/ throw '>ExceptionMarker<';
}

View file

@ -12,12 +12,12 @@ test() async {
// TODO(johnniwinther): Investigate why kernel doesn't point to the body
// start brace.
// ignore: UNUSED_LOCAL_VARIABLE
var /*2:test*/ c = new /*3:test*/ Class();
var /*2:test*/ /*3:test*/ c = new /*4:test*/ Class();
}
class Class {
@NoInline()
/*4:Class*/ Class() {
/*5:Class*/ throw '>ExceptionMarker<';
/*5:Class*/ Class() {
/*6:Class*/ throw '>ExceptionMarker<';
}
}

View file

@ -9,11 +9,11 @@ main() {
}
@NoInline()
test1() async /*2:test1*/ {
/*3:test1*/ test2();
test1() async /*2:test1*/ /*kernel.3:test1*/ {
/*9:test1*/ test2();
}
@NoInline()
test2() {
/*4:test2*/ throw '>ExceptionMarker<';
/*10:test2*/ throw '>ExceptionMarker<';
}

View file

@ -12,5 +12,5 @@ main() {
@NoInline()
test() async {
await null;
/*1:test*/ throw '>ExceptionMarker<';
/*9:test*/ throw '>ExceptionMarker<';
}

View file

@ -222,7 +222,6 @@ uri_test: RuntimeError
[ $compiler == dart2js && $fasta ]
int_from_environment_test: Pass # Issue 31762
int_parse_radix_test/none: Pass # Issue 31762
map_entry_test: RuntimeError
[ $compiler == dart2js && $fasta && $host_checked && $strong ]
cast_test: RuntimeError

View file

@ -511,17 +511,11 @@ type_parameter_test/09: Crash # Internal Error: Unexpected type variable in stat
type_variable_scope_test/03: Crash # Internal Error: Unexpected type variable in static context.
[ $compiler == dart2js && !$checked ]
async_covariant_type_test: RuntimeError # checked / strong mode test
async_dcall_type_test: RuntimeError # checked / strong mode test
asyncstar_covariant_type_test: RuntimeError # checked / strong mode test
asyncstar_dcall_type_test: RuntimeError # checked / strong mode test
covariance_field_test/01: RuntimeError
covariance_field_test/02: RuntimeError
covariance_field_test/03: RuntimeError
covariance_field_test/04: RuntimeError
covariance_field_test/05: RuntimeError
syncstar_covariant_type_test: RuntimeError # checked / strong mode test
syncstar_dcall_type_test: RuntimeError # checked / strong mode test
[ $compiler == dart2js && !$checked && !$enable_asserts ]
assertion_test: RuntimeError, OK
@ -1057,7 +1051,6 @@ function_subtype_not1_test: RuntimeError
function_subtype_setter0_test: RuntimeError
function_type_alias2_test: RuntimeError
function_type_alias4_test: RuntimeError
generic_async_star_test: RuntimeError
generic_closure_test/01: RuntimeError
generic_closure_test/none: RuntimeError
generic_function_bounds_test: RuntimeError
@ -1610,7 +1603,6 @@ function_subtype_not1_test: RuntimeError
function_subtype_setter0_test: RuntimeError
function_type_alias2_test: RuntimeError
function_type_alias4_test: RuntimeError
generic_async_star_test: RuntimeError
generic_function_bounds_test: RuntimeError
generic_function_dcall_test: RuntimeError
generic_function_type_as_type_argument_test/01: MissingCompileTimeError