|  | // Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | import 'package:js_shared/variance.dart'; | 
|  | import 'package:kernel/ast.dart' as ir; | 
|  |  | 
|  | import '../closure.dart'; | 
|  | import '../common.dart'; | 
|  | import '../elements/entities.dart'; | 
|  | import '../elements/names.dart'; | 
|  | import '../elements/types.dart'; | 
|  | import '../ir/closure.dart'; | 
|  | import '../ir/element_map.dart'; | 
|  | import '../js_backend/annotations.dart'; | 
|  | import '../js_model/element_map.dart'; | 
|  | import '../ordered_typeset.dart'; | 
|  | import '../serialization/deferrable.dart'; | 
|  | import '../serialization/serialization.dart'; | 
|  | import '../universe/selector.dart'; | 
|  | import 'class_type_variable_access.dart'; | 
|  | import 'elements.dart'; | 
|  | import 'env.dart'; | 
|  | import 'js_world_builder.dart' show JClosedWorldBuilder; | 
|  |  | 
|  | class ClosureDataImpl implements ClosureData { | 
|  | /// Tag used for identifying serialized [ClosureData] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'closure-data'; | 
|  |  | 
|  | final JsToElementMap _elementMap; | 
|  |  | 
|  | /// Map of the scoping information that corresponds to a particular entity. | 
|  | final Map<MemberEntity, ScopeInfo> _scopeMap; | 
|  | final Deferrable<Map<ir.TreeNode, CapturedScope>> _capturedScopesMap; | 
|  | // Indicates the type variables (if any) that are captured in a given | 
|  | // Signature function. | 
|  | final Deferrable<Map<MemberEntity, CapturedScope>> | 
|  | _capturedScopeForSignatureMap; | 
|  |  | 
|  | final Deferrable<Map<ir.LocalFunction, ClosureRepresentationInfo>> | 
|  | _localClosureRepresentationMap; | 
|  |  | 
|  | final Map<MemberEntity, MemberEntity> _enclosingMembers; | 
|  |  | 
|  | ClosureDataImpl( | 
|  | this._elementMap, | 
|  | this._scopeMap, | 
|  | Map<ir.TreeNode, CapturedScope> capturedScopesMap, | 
|  | Map<MemberEntity, CapturedScope> capturedScopeForSignatureMap, | 
|  | Map<ir.LocalFunction, ClosureRepresentationInfo> | 
|  | localClosureRepresentationMap, | 
|  | this._enclosingMembers, | 
|  | ) : _capturedScopesMap = Deferrable.eager(capturedScopesMap), | 
|  | _capturedScopeForSignatureMap = Deferrable.eager( | 
|  | capturedScopeForSignatureMap, | 
|  | ), | 
|  | _localClosureRepresentationMap = Deferrable.eager( | 
|  | localClosureRepresentationMap, | 
|  | ); | 
|  |  | 
|  | ClosureDataImpl._deserialized( | 
|  | this._elementMap, | 
|  | this._scopeMap, | 
|  | this._capturedScopesMap, | 
|  | this._capturedScopeForSignatureMap, | 
|  | this._localClosureRepresentationMap, | 
|  | this._enclosingMembers, | 
|  | ); | 
|  |  | 
|  | static Map<ir.TreeNode, CapturedScope> _readCapturedScopesMap( | 
|  | DataSourceReader source, | 
|  | ) { | 
|  | return source.readTreeNodeMap( | 
|  | () => CapturedScope.readFromDataSource(source), | 
|  | ); | 
|  | } | 
|  |  | 
|  | static Map<MemberEntity, CapturedScope> _readCapturedScopeForSignatureMap( | 
|  | DataSourceReader source, | 
|  | ) { | 
|  | return source.readMemberMap( | 
|  | (_) => CapturedScope.readFromDataSource(source), | 
|  | ); | 
|  | } | 
|  |  | 
|  | static Map<ir.LocalFunction, ClosureRepresentationInfo> | 
|  | _readLocalClosureRepresentationMap(DataSourceReader source) { | 
|  | return source.readTreeNodeMap<ir.LocalFunction, ClosureRepresentationInfo>( | 
|  | () => ClosureRepresentationInfo.readFromDataSource(source), | 
|  | ); | 
|  | } | 
|  |  | 
|  | /// Deserializes a [ClosureData] object from [source]. | 
|  | factory ClosureDataImpl.readFromDataSource( | 
|  | JsToElementMap elementMap, | 
|  | DataSourceReader source, | 
|  | ) { | 
|  | source.begin(tag); | 
|  | // TODO(johnniwinther): Support shared [ScopeInfo]. | 
|  | final scopeMap = source.readMemberMap( | 
|  | (MemberEntity member) => | 
|  | source.readDeferrable(ScopeInfo.readFromDataSource), | 
|  | ); | 
|  | final capturedScopesMap = source.readDeferrable(_readCapturedScopesMap); | 
|  | final capturedScopeForSignatureMap = source.readDeferrable( | 
|  | _readCapturedScopeForSignatureMap, | 
|  | ); | 
|  | final localClosureRepresentationMap = source.readDeferrable( | 
|  | _readLocalClosureRepresentationMap, | 
|  | ); | 
|  | Map<MemberEntity, MemberEntity> enclosingMembers = source.readMemberMap( | 
|  | (member) => source.readMember(), | 
|  | ); | 
|  | source.end(tag); | 
|  | return ClosureDataImpl._deserialized( | 
|  | elementMap, | 
|  | DeferrableValueMap(scopeMap), | 
|  | capturedScopesMap, | 
|  | capturedScopeForSignatureMap, | 
|  | localClosureRepresentationMap, | 
|  | enclosingMembers, | 
|  | ); | 
|  | } | 
|  |  | 
|  | /// Serializes this [ClosureData] to [sink]. | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.begin(tag); | 
|  | sink.writeMemberMap( | 
|  | _scopeMap, | 
|  | (_, ScopeInfo info) => | 
|  | sink.writeDeferrable(() => info.writeToDataSink(sink)), | 
|  | ); | 
|  | sink.writeDeferrable( | 
|  | () => sink.writeTreeNodeMap(_capturedScopesMap.loaded(), ( | 
|  | CapturedScope scope, | 
|  | ) { | 
|  | scope.writeToDataSink(sink); | 
|  | }), | 
|  | ); | 
|  | sink.writeDeferrable( | 
|  | () => sink.writeMemberMap( | 
|  | _capturedScopeForSignatureMap.loaded(), | 
|  | (MemberEntity member, CapturedScope scope) => | 
|  | scope.writeToDataSink(sink), | 
|  | ), | 
|  | ); | 
|  | sink.writeDeferrable( | 
|  | () => sink.writeTreeNodeMap(_localClosureRepresentationMap.loaded(), ( | 
|  | ClosureRepresentationInfo info, | 
|  | ) { | 
|  | info.writeToDataSink(sink); | 
|  | }), | 
|  | ); | 
|  | sink.writeMemberMap(_enclosingMembers, ( | 
|  | MemberEntity member, | 
|  | MemberEntity value, | 
|  | ) { | 
|  | sink.writeMember(value); | 
|  | }); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | @override | 
|  | ScopeInfo getScopeInfo(MemberEntity entity) { | 
|  | // TODO(johnniwinther): Remove this check when constructor bodies a created | 
|  | // eagerly with the J-model; a constructor body should have it's own | 
|  | // [ClosureRepresentationInfo]. | 
|  | if (entity is ConstructorBodyEntity) { | 
|  | entity = entity.constructor; | 
|  | } else if (entity is JParameterStub) { | 
|  | entity = entity.target; | 
|  | } | 
|  |  | 
|  | return _scopeMap[entity]!; | 
|  | } | 
|  |  | 
|  | // TODO(efortuna): Eventually capturedScopesMap[node] should always | 
|  | // be non-null, and we should just test that with an assert. | 
|  | @override | 
|  | CapturedScope getCapturedScope(MemberEntity entity) { | 
|  | MemberDefinition definition = _elementMap.getMemberDefinition(entity); | 
|  | switch (definition.kind) { | 
|  | case MemberKind.regular: | 
|  | case MemberKind.constructor: | 
|  | case MemberKind.constructorBody: | 
|  | case MemberKind.closureCall: | 
|  | return _capturedScopesMap.loaded()[definition.node] ?? | 
|  | const CapturedScope(); | 
|  | case MemberKind.signature: | 
|  | return _capturedScopeForSignatureMap.loaded()[entity] ?? | 
|  | const CapturedScope(); | 
|  | case MemberKind.parameterStub: | 
|  | return const CapturedScope(); | 
|  | case MemberKind.closureField: | 
|  | case MemberKind.generatorBody: | 
|  | case MemberKind.recordGetter: | 
|  | throw failedAt(entity, "Unexpected member definition $definition"); | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | CapturedScope getCapturedBlockScope(ir.Block blockNode) => | 
|  | _capturedScopesMap.loaded()[blockNode] ?? const CapturedScope(); | 
|  |  | 
|  | @override | 
|  | // TODO(efortuna): Eventually capturedScopesMap[node] should always | 
|  | // be non-null, and we should just test that with an assert. | 
|  | CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => | 
|  | _capturedScopesMap.loaded()[loopNode] as CapturedLoopScope? ?? | 
|  | const CapturedLoopScope(); | 
|  |  | 
|  | @override | 
|  | ClosureRepresentationInfo getClosureInfo(ir.LocalFunction node) => | 
|  | _localClosureRepresentationMap.loaded()[node]!; | 
|  |  | 
|  | @override | 
|  | MemberEntity getEnclosingMember(MemberEntity member) { | 
|  | return _enclosingMembers[member] ?? member; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Closure conversion code using our new Entity model. Closure conversion is | 
|  | /// necessary because the semantics of closures are slightly different in Dart | 
|  | /// than JavaScript. Closure conversion is separated out into two phases: | 
|  | /// generation of a new (temporary) representation to store where variables need | 
|  | /// to be hoisted/captured up at another level to re-write the closure, and then | 
|  | /// the code generation phase where we generate elements and/or instructions to | 
|  | /// represent this new code path. | 
|  | /// | 
|  | /// For a general explanation of how closure conversion works at a high level, | 
|  | /// check out: | 
|  | /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or | 
|  | /// http://matt.might.net/articles/closure-conversion/. | 
|  |  | 
|  | class ClosureDataBuilder { | 
|  | final DiagnosticReporter _reporter; | 
|  | final JsToElementMap _elementMap; | 
|  | final AnnotationsData _annotationsData; | 
|  |  | 
|  | /// Map of the scoping information that corresponds to a particular entity. | 
|  | final Map<MemberEntity, ScopeInfo> _scopeMap = {}; | 
|  | final Map<ir.TreeNode, CapturedScope> _capturedScopesMap = {}; | 
|  | // Indicates the type variables (if any) that are captured in a given | 
|  | // Signature function. | 
|  | final Map<MemberEntity, CapturedScope> _capturedScopeForSignatureMap = {}; | 
|  |  | 
|  | final Map<ir.LocalFunction, ClosureRepresentationInfo> | 
|  | _localClosureRepresentationMap = {}; | 
|  |  | 
|  | final Map<MemberEntity, MemberEntity> _enclosingMembers = {}; | 
|  |  | 
|  | ClosureDataBuilder(this._reporter, this._elementMap, this._annotationsData); | 
|  |  | 
|  | void _updateScopeBasedOnRtiNeed( | 
|  | KernelScopeInfo scope, | 
|  | ClosureRtiNeed rtiNeed, | 
|  | MemberEntity outermostEntity, | 
|  | ) { | 
|  | bool includeForRti(Set<VariableUse> useSet) { | 
|  | for (VariableUse usage in useSet) { | 
|  | switch (usage) { | 
|  | case SimpleVariableUse.explicit: | 
|  | return true; | 
|  | case SimpleVariableUse.implicitCast: | 
|  | if (_annotationsData | 
|  | .getImplicitDowncastCheckPolicy(outermostEntity) | 
|  | .isEmitted) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case SimpleVariableUse.localType: | 
|  | break; | 
|  | case ConstructorTypeArgumentVariableUse(:final member): | 
|  | ConstructorEntity constructor = _elementMap.getConstructor(member); | 
|  | if (rtiNeed.classNeedsTypeArguments(constructor.enclosingClass)) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case StaticTypeArgumentVariableUse(:final procedure): | 
|  | FunctionEntity method = _elementMap.getMethod(procedure); | 
|  | if (rtiNeed.methodNeedsTypeArguments(method)) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case InstanceTypeArgumentVariableUse(:final invocation): | 
|  | Selector selector = _elementMap.getSelector(invocation); | 
|  | if (rtiNeed.selectorNeedsTypeArguments(selector)) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case LocalTypeArgumentVariableUse( | 
|  | :final localFunction, | 
|  | :final invocation, | 
|  | ): | 
|  | // TODO(johnniwinther): We should be able to track direct local | 
|  | // function invocations and not have to use the selector here. | 
|  | Selector selector = _elementMap.getSelector(invocation); | 
|  | if (rtiNeed.localFunctionNeedsTypeArguments(localFunction) || | 
|  | rtiNeed.selectorNeedsTypeArguments(selector)) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case MemberParameterVariableUse(:final member): | 
|  | if (_annotationsData | 
|  | .getParameterCheckPolicy(outermostEntity) | 
|  | .isEmitted) { | 
|  | return true; | 
|  | } else { | 
|  | FunctionEntity method = _elementMap.getMethod( | 
|  | member as ir.Procedure, | 
|  | ); | 
|  | if (rtiNeed.methodNeedsSignature(method)) { | 
|  | return true; | 
|  | } | 
|  | if (rtiNeed.methodNeedsTypeArguments(method)) { | 
|  | // Stubs generated for this method might make use of this type | 
|  | // parameter for default type arguments. | 
|  | return true; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case LocalParameterVariableUse(:final localFunction): | 
|  | if (_annotationsData | 
|  | .getParameterCheckPolicy(outermostEntity) | 
|  | .isEmitted) { | 
|  | return true; | 
|  | } else if (rtiNeed.localFunctionNeedsSignature(localFunction)) { | 
|  | return true; | 
|  | } else if (rtiNeed.localFunctionNeedsTypeArguments(localFunction)) { | 
|  | // Stubs generated for this local function might make use of this | 
|  | // type parameter for default type arguments. | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case MemberReturnTypeVariableUse(:final member): | 
|  | FunctionEntity method = _elementMap.getMethod( | 
|  | member as ir.Procedure, | 
|  | ); | 
|  | if (rtiNeed.methodNeedsSignature(method)) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case LocalReturnTypeVariableUse(:final localFunction): | 
|  | if (localFunction.function.asyncMarker != ir.AsyncMarker.Sync) { | 
|  | // The Future/Iterator/Stream implementation requires the type. | 
|  | return true; | 
|  | } | 
|  | if (rtiNeed.localFunctionNeedsSignature(localFunction)) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case SimpleVariableUse.fieldType: | 
|  | if (_annotationsData | 
|  | .getParameterCheckPolicy(outermostEntity) | 
|  | .isEmitted) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case SimpleVariableUse.listLiteral: | 
|  | if (rtiNeed.classNeedsTypeArguments( | 
|  | _elementMap.commonElements.jsArrayClass, | 
|  | )) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case SimpleVariableUse.setLiteral: | 
|  | if (rtiNeed.classNeedsTypeArguments( | 
|  | _elementMap.commonElements.setLiteralClass, | 
|  | )) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case SimpleVariableUse.mapLiteral: | 
|  | if (rtiNeed.classNeedsTypeArguments( | 
|  | _elementMap.commonElements.mapLiteralClass, | 
|  | )) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case InstantiationTypeArgumentVariableUse(:final instantiation): | 
|  | // TODO(johnniwinther): Use the static type of the expression. | 
|  | if (rtiNeed.instantiationNeedsTypeArguments( | 
|  | null, | 
|  | instantiation.typeArguments.length, | 
|  | )) { | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (includeForRti(scope.thisUsedAsFreeVariableIfNeedsRti)) { | 
|  | scope.thisUsedAsFreeVariable = true; | 
|  | } | 
|  | scope.freeVariablesForRti.forEach(( | 
|  | TypeVariableTypeWithContext typeVariable, | 
|  | Set<VariableUse> useSet, | 
|  | ) { | 
|  | if (includeForRti(useSet)) { | 
|  | scope.freeVariables.add(typeVariable); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | ClosureData createClosureEntities( | 
|  | JClosedWorldBuilder closedWorldBuilder, | 
|  | Map<MemberEntity, ClosureScopeModel> closureModels, | 
|  | ClosureRtiNeed rtiNeed, | 
|  | List<FunctionEntity> callMethods, | 
|  | ) { | 
|  | void processModel(MemberEntity member, ClosureScopeModel model) { | 
|  | Map<ir.VariableDeclaration, JContextField> allBoxedVariables = _elementMap | 
|  | .makeContextContainer(model.scopeInfo!, member); | 
|  | _scopeMap[member] = JsScopeInfo.from( | 
|  | allBoxedVariables, | 
|  | model.scopeInfo!, | 
|  | member.enclosingClass, | 
|  | ); | 
|  |  | 
|  | model.capturedScopesMap.forEach(( | 
|  | ir.Node node, | 
|  | KernelCapturedScope scope, | 
|  | ) { | 
|  | Map<ir.VariableDeclaration, JContextField> boxedVariables = _elementMap | 
|  | .makeContextContainer(scope, member); | 
|  | _updateScopeBasedOnRtiNeed(scope, rtiNeed, member); | 
|  |  | 
|  | if (scope is KernelCapturedLoopScope) { | 
|  | _capturedScopesMap[node as ir.TreeNode] = JsCapturedLoopScope.from( | 
|  | boxedVariables, | 
|  | scope, | 
|  | member.enclosingClass, | 
|  | ); | 
|  | } else { | 
|  | _capturedScopesMap[node as ir.TreeNode] = JsCapturedScope.from( | 
|  | boxedVariables, | 
|  | scope, | 
|  | member.enclosingClass, | 
|  | ); | 
|  | } | 
|  | allBoxedVariables.addAll(boxedVariables); | 
|  | }); | 
|  |  | 
|  | Map<ir.LocalFunction, KernelScopeInfo> closuresToGenerate = | 
|  | model.closuresToGenerate; | 
|  | for (ir.LocalFunction node in closuresToGenerate.keys) { | 
|  | ir.FunctionNode functionNode = node.function; | 
|  | JsClosureClassInfo closureClassInfo = _produceSyntheticElements( | 
|  | closedWorldBuilder, | 
|  | member, | 
|  | functionNode, | 
|  | closuresToGenerate[node]!, | 
|  | allBoxedVariables, | 
|  | rtiNeed, | 
|  | createSignatureMethod: rtiNeed.localFunctionNeedsSignature( | 
|  | functionNode.parent as ir.LocalFunction, | 
|  | ), | 
|  | ); | 
|  | // Add also for the call method. | 
|  | _scopeMap[closureClassInfo.callMethod!] = closureClassInfo; | 
|  | if (closureClassInfo.signatureMethod != null) { | 
|  | _scopeMap[closureClassInfo.signatureMethod!] = closureClassInfo; | 
|  |  | 
|  | // Set up capturedScope for signature method. This is distinct from | 
|  | // _capturedScopesMap because there is no corresponding ir.Node for | 
|  | // the signature. | 
|  | if (rtiNeed.localFunctionNeedsSignature( | 
|  | functionNode.parent as ir.LocalFunction, | 
|  | ) && | 
|  | model.capturedScopesMap[functionNode] != null) { | 
|  | KernelCapturedScope capturedScope = | 
|  | model.capturedScopesMap[functionNode]!; | 
|  | assert(capturedScope is! KernelCapturedLoopScope); | 
|  | KernelCapturedScope signatureCapturedScope = | 
|  | KernelCapturedScope.forSignature(capturedScope); | 
|  | _updateScopeBasedOnRtiNeed(signatureCapturedScope, rtiNeed, member); | 
|  | _capturedScopeForSignatureMap[closureClassInfo.signatureMethod!] = | 
|  | JsCapturedScope.from( | 
|  | {}, | 
|  | signatureCapturedScope, | 
|  | member.enclosingClass, | 
|  | ); | 
|  | } | 
|  | } | 
|  | callMethods.add(closureClassInfo.callMethod!); | 
|  | } | 
|  | } | 
|  |  | 
|  | closureModels.forEach((MemberEntity member, ClosureScopeModel model) { | 
|  | _reporter.withCurrentElement(member, () { | 
|  | processModel(member, model); | 
|  | }); | 
|  | }); | 
|  | return ClosureDataImpl( | 
|  | _elementMap, | 
|  | _scopeMap, | 
|  | _capturedScopesMap, | 
|  | _capturedScopeForSignatureMap, | 
|  | _localClosureRepresentationMap, | 
|  | _enclosingMembers, | 
|  | ); | 
|  | } | 
|  |  | 
|  | /// Given what variables are captured at each point, construct closure classes | 
|  | /// with fields containing the captured variables to replicate the Dart | 
|  | /// closure semantics in JS. If this closure captures any variables (meaning | 
|  | /// the closure accesses a variable that gets accessed at some point), then | 
|  | /// boxForCapturedVariables stores the local context for those variables. | 
|  | /// If no variables are captured, this parameter is null. | 
|  | JsClosureClassInfo _produceSyntheticElements( | 
|  | JClosedWorldBuilder closedWorldBuilder, | 
|  | MemberEntity member, | 
|  | ir.FunctionNode node, | 
|  | KernelScopeInfo info, | 
|  | Map<ir.VariableDeclaration, JContextField> boxedVariables, | 
|  | ClosureRtiNeed rtiNeed, { | 
|  | required bool createSignatureMethod, | 
|  | }) { | 
|  | _updateScopeBasedOnRtiNeed(info, rtiNeed, member); | 
|  | JsClosureClassInfo closureClassInfo = closedWorldBuilder.buildClosureClass( | 
|  | member, | 
|  | node, | 
|  | member.library as JLibrary, | 
|  | boxedVariables, | 
|  | info, | 
|  | createSignatureMethod: createSignatureMethod, | 
|  | ); | 
|  |  | 
|  | // We want the original declaration where that function is used to point | 
|  | // to the correct closure class. | 
|  | _enclosingMembers[closureClassInfo.callMethod!] = member; | 
|  | if (closureClassInfo.signatureMethod != null) { | 
|  | _enclosingMembers[closureClassInfo.signatureMethod!] = member; | 
|  | } | 
|  | if (node.parent is ir.Member) { | 
|  | assert(_elementMap.getMember(node.parent as ir.Member) == member); | 
|  | } else { | 
|  | assert(node.parent is ir.LocalFunction); | 
|  | _localClosureRepresentationMap[node.parent as ir.LocalFunction] = | 
|  | closureClassInfo; | 
|  | } | 
|  | return closureClassInfo; | 
|  | } | 
|  | } | 
|  |  | 
|  | class JsScopeInfo extends ScopeInfo { | 
|  | /// Tag used for identifying serialized [JsScopeInfo] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'scope-info'; | 
|  |  | 
|  | final Iterable<ir.VariableDeclaration> _localsUsedInTryOrSync; | 
|  |  | 
|  | Set<Local>? _localsUsedInTryOrSyncCache; | 
|  |  | 
|  | @override | 
|  | final Local? thisLocal; | 
|  |  | 
|  | final Map<ir.VariableDeclaration, JContextField> _boxedVariables; | 
|  |  | 
|  | Map<Local, JContextField>? _boxedVariablesCache; | 
|  |  | 
|  | JsScopeInfo.internal( | 
|  | this._localsUsedInTryOrSync, | 
|  | this.thisLocal, | 
|  | this._boxedVariables, | 
|  | ); | 
|  |  | 
|  | JsScopeInfo.from( | 
|  | this._boxedVariables, | 
|  | KernelScopeInfo info, | 
|  | ClassEntity? enclosingClass, | 
|  | ) : thisLocal = info.hasThisLocal ? ThisLocal(enclosingClass!) : null, | 
|  | _localsUsedInTryOrSync = info.localsUsedInTryOrSync; | 
|  |  | 
|  | void _ensureBoxedVariableCache(KernelToLocalsMap localsMap) { | 
|  | if (_boxedVariablesCache == null) { | 
|  | if (_boxedVariables.isEmpty) { | 
|  | _boxedVariablesCache = const {}; | 
|  | } else { | 
|  | final cache = <Local, JContextField>{}; | 
|  | _boxedVariables.forEach(( | 
|  | ir.VariableDeclaration node, | 
|  | JContextField field, | 
|  | ) { | 
|  | cache[localsMap.getLocalVariable(node)] = field; | 
|  | }); | 
|  | _boxedVariablesCache = cache; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | void forEachBoxedVariable( | 
|  | KernelToLocalsMap localsMap, | 
|  | void Function(Local local, FieldEntity field) f, | 
|  | ) { | 
|  | _ensureBoxedVariableCache(localsMap); | 
|  | _boxedVariablesCache!.forEach(f); | 
|  | } | 
|  |  | 
|  | @override | 
|  | bool localIsUsedInTryOrSync(KernelToLocalsMap localsMap, Local variable) { | 
|  | if (_localsUsedInTryOrSyncCache == null) { | 
|  | if (_localsUsedInTryOrSync.isEmpty) { | 
|  | _localsUsedInTryOrSyncCache = const {}; | 
|  | } else { | 
|  | _localsUsedInTryOrSyncCache = {}; | 
|  | for (ir.VariableDeclaration node in _localsUsedInTryOrSync) { | 
|  | _localsUsedInTryOrSyncCache!.add(localsMap.getLocalVariable(node)); | 
|  | } | 
|  | } | 
|  | } | 
|  | return _localsUsedInTryOrSyncCache!.contains(variable); | 
|  | } | 
|  |  | 
|  | @override | 
|  | String toString() { | 
|  | StringBuffer sb = StringBuffer(); | 
|  | sb.write('this=$thisLocal,'); | 
|  | sb.write('localsUsedInTryOrSync={${_localsUsedInTryOrSync.join(', ')}}'); | 
|  | return sb.toString(); | 
|  | } | 
|  |  | 
|  | @override | 
|  | bool isBoxedVariable(KernelToLocalsMap localsMap, Local variable) { | 
|  | _ensureBoxedVariableCache(localsMap); | 
|  | return _boxedVariablesCache!.containsKey(variable); | 
|  | } | 
|  |  | 
|  | factory JsScopeInfo.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | Iterable<ir.VariableDeclaration> localsUsedInTryOrSync = source | 
|  | .readTreeNodes<ir.VariableDeclaration>(); | 
|  | Local? thisLocal = source.readLocalOrNull(); | 
|  | Map<ir.VariableDeclaration, JContextField> boxedVariables = source | 
|  | .readTreeNodeMap<ir.VariableDeclaration, JContextField>( | 
|  | () => source.readMember() as JContextField, | 
|  | ); | 
|  | source.end(tag); | 
|  | if (boxedVariables.isEmpty) boxedVariables = const {}; | 
|  | return JsScopeInfo.internal( | 
|  | localsUsedInTryOrSync, | 
|  | thisLocal, | 
|  | boxedVariables, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(ScopeInfoKind.scopeInfo); | 
|  | sink.begin(tag); | 
|  | sink.writeTreeNodes(_localsUsedInTryOrSync); | 
|  | sink.writeLocalOrNull(thisLocal); | 
|  | sink.writeTreeNodeMap(_boxedVariables, sink.writeMember); | 
|  | sink.end(tag); | 
|  | } | 
|  | } | 
|  |  | 
|  | class JsCapturedScope extends JsScopeInfo implements CapturedScope { | 
|  | /// Tag used for identifying serialized [JsCapturedScope] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'captured-scope'; | 
|  |  | 
|  | @override | 
|  | final Local? contextBox; | 
|  |  | 
|  | JsCapturedScope.internal( | 
|  | super.localsUsedInTryOrSync, | 
|  | super.thisLocal, | 
|  | super.boxedVariables, | 
|  | this.contextBox, | 
|  | ) : super.internal(); | 
|  |  | 
|  | JsCapturedScope.from( | 
|  | super.boxedVariables, | 
|  | super.capturedScope, | 
|  | super.enclosingClass, | 
|  | ) : contextBox = boxedVariables.isNotEmpty | 
|  | ? boxedVariables.values.first.box | 
|  | : null, | 
|  | super.from(); | 
|  |  | 
|  | @override | 
|  | bool get requiresContextBox => _boxedVariables.isNotEmpty; | 
|  |  | 
|  | factory JsCapturedScope.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | Iterable<ir.VariableDeclaration> localsUsedInTryOrSync = source | 
|  | .readTreeNodes<ir.VariableDeclaration>(); | 
|  | Local? thisLocal = source.readLocalOrNull(); | 
|  | Map<ir.VariableDeclaration, JContextField> boxedVariables = source | 
|  | .readTreeNodeMap<ir.VariableDeclaration, JContextField>( | 
|  | () => source.readMember() as JContextField, | 
|  | ); | 
|  | Local? context = source.readLocalOrNull(); | 
|  | source.end(tag); | 
|  | return JsCapturedScope.internal( | 
|  | localsUsedInTryOrSync, | 
|  | thisLocal, | 
|  | boxedVariables, | 
|  | context, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(ScopeInfoKind.capturedScope); | 
|  | sink.begin(tag); | 
|  | sink.writeTreeNodes(_localsUsedInTryOrSync); | 
|  | sink.writeLocalOrNull(thisLocal); | 
|  | sink.writeTreeNodeMap(_boxedVariables, sink.writeMember); | 
|  | sink.writeLocalOrNull(contextBox); | 
|  | sink.end(tag); | 
|  | } | 
|  | } | 
|  |  | 
|  | class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { | 
|  | /// Tag used for identifying serialized [JsCapturedLoopScope] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'captured-loop-scope'; | 
|  |  | 
|  | final List<ir.VariableDeclaration> _boxedLoopVariables; | 
|  |  | 
|  | JsCapturedLoopScope.internal( | 
|  | super.localsUsedInTryOrSync, | 
|  | super.thisLocal, | 
|  | super.boxedVariables, | 
|  | super.context, | 
|  | this._boxedLoopVariables, | 
|  | ) : super.internal(); | 
|  |  | 
|  | JsCapturedLoopScope.from( | 
|  | super.boxedVariables, | 
|  | KernelCapturedLoopScope super.capturedScope, | 
|  | super.enclosingClass, | 
|  | ) : _boxedLoopVariables = capturedScope.boxedLoopVariables, | 
|  | super.from(); | 
|  |  | 
|  | @override | 
|  | bool get hasBoxedLoopVariables => _boxedLoopVariables.isNotEmpty; | 
|  |  | 
|  | factory JsCapturedLoopScope.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | Iterable<ir.VariableDeclaration> localsUsedInTryOrSync = source | 
|  | .readTreeNodes<ir.VariableDeclaration>(); | 
|  | Local? thisLocal = source.readLocalOrNull(); | 
|  | Map<ir.VariableDeclaration, JContextField> boxedVariables = source | 
|  | .readTreeNodeMap<ir.VariableDeclaration, JContextField>( | 
|  | () => source.readMember() as JContextField, | 
|  | ); | 
|  | Local? context = source.readLocalOrNull(); | 
|  | List<ir.VariableDeclaration> boxedLoopVariables = source | 
|  | .readTreeNodes<ir.VariableDeclaration>(); | 
|  | source.end(tag); | 
|  | return JsCapturedLoopScope.internal( | 
|  | localsUsedInTryOrSync, | 
|  | thisLocal, | 
|  | boxedVariables, | 
|  | context, | 
|  | boxedLoopVariables, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(ScopeInfoKind.capturedLoopScope); | 
|  | sink.begin(tag); | 
|  | sink.writeTreeNodes(_localsUsedInTryOrSync); | 
|  | sink.writeLocalOrNull(thisLocal); | 
|  | sink.writeTreeNodeMap(_boxedVariables, sink.writeMember); | 
|  | sink.writeLocalOrNull(contextBox); | 
|  | sink.writeTreeNodes(_boxedLoopVariables); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | @override | 
|  | List<Local> getBoxedLoopVariables(KernelToLocalsMap localsMap) { | 
|  | List<Local> locals = []; | 
|  | for (ir.VariableDeclaration boxedLoopVariable in _boxedLoopVariables) { | 
|  | locals.add(localsMap.getLocalVariable(boxedLoopVariable)); | 
|  | } | 
|  | return locals; | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 
|  | class JsClosureClassInfo extends JsScopeInfo | 
|  | implements ClosureRepresentationInfo { | 
|  | /// Tag used for identifying serialized [JsClosureClassInfo] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'closure-representation-info'; | 
|  |  | 
|  | @override | 
|  | JFunction? callMethod; | 
|  |  | 
|  | @override | 
|  | JSignatureMethod? signatureMethod; | 
|  |  | 
|  | /// The local used for this closure, if it is an anonymous closure, i.e. | 
|  | /// an `ir.FunctionExpression`. | 
|  | final Local? _closureEntity; | 
|  |  | 
|  | /// The local variable that defines this closure, if it is a local function | 
|  | /// declaration. | 
|  | final ir.VariableDeclaration? _closureEntityVariable; | 
|  |  | 
|  | @override | 
|  | final JClass closureClassEntity; | 
|  |  | 
|  | final Map<ir.VariableDeclaration, JField> _variableToFieldMap; | 
|  | final Map<JTypeVariable, JField> _typeVariableToFieldMap; | 
|  | final Map<Local, JField> _localToFieldMap; | 
|  | Map<JField, Local>? _fieldToLocalsMap; | 
|  |  | 
|  | JsClosureClassInfo.internal( | 
|  | super.localsUsedInTryOrSync, | 
|  | super.thisLocal, | 
|  | super.boxedVariables, | 
|  | this.callMethod, | 
|  | this.signatureMethod, | 
|  | this._closureEntity, | 
|  | this._closureEntityVariable, | 
|  | this.closureClassEntity, | 
|  | this._variableToFieldMap, | 
|  | this._typeVariableToFieldMap, | 
|  | this._localToFieldMap, | 
|  | ) : super.internal(); | 
|  |  | 
|  | JsClosureClassInfo.fromScopeInfo( | 
|  | this.closureClassEntity, | 
|  | ir.FunctionNode closureSourceNode, | 
|  | Map<ir.VariableDeclaration, JContextField> boxedVariables, | 
|  | KernelScopeInfo info, | 
|  | ClassEntity? enclosingClass, | 
|  | this._closureEntity, | 
|  | this._closureEntityVariable, | 
|  | ) : _variableToFieldMap = {}, | 
|  | _typeVariableToFieldMap = {}, | 
|  | _localToFieldMap = {}, | 
|  | super.from(boxedVariables, info, enclosingClass); | 
|  |  | 
|  | factory JsClosureClassInfo.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | Iterable<ir.VariableDeclaration> localsUsedInTryOrSync = source | 
|  | .readTreeNodes<ir.VariableDeclaration>(); | 
|  | Local? thisLocal = source.readLocalOrNull(); | 
|  | Map<ir.VariableDeclaration, JContextField> boxedVariables = source | 
|  | .readTreeNodeMap<ir.VariableDeclaration, JContextField>( | 
|  | () => source.readMember() as JContextField, | 
|  | ); | 
|  | JFunction callMethod = source.readMember() as JFunction; | 
|  | JSignatureMethod? signatureMethod = | 
|  | source.readMemberOrNull() as JSignatureMethod?; | 
|  | Local? closureEntity = source.readLocalOrNull(); | 
|  | ir.VariableDeclaration? closureEntityVariable = | 
|  | source.readTreeNodeOrNull() as ir.VariableDeclaration?; | 
|  | JClass closureClassEntity = source.readClass() as JClass; | 
|  | Map<ir.VariableDeclaration, JField> localToFieldMap = source | 
|  | .readTreeNodeMap<ir.VariableDeclaration, JField>( | 
|  | () => source.readMember() as JField, | 
|  | ); | 
|  | Map<JTypeVariable, JField> typeVariableToFieldMap = source | 
|  | .readTypeVariableMap<JTypeVariable, JField>( | 
|  | () => source.readMember() as JField, | 
|  | ); | 
|  | Map<Local, JField> thisLocalToFieldMap = source.readLocalMap( | 
|  | () => source.readMember() as JField, | 
|  | ); | 
|  | source.end(tag); | 
|  | if (boxedVariables.isEmpty) boxedVariables = const {}; | 
|  | if (localToFieldMap.isEmpty) localToFieldMap = const {}; | 
|  | return JsClosureClassInfo.internal( | 
|  | localsUsedInTryOrSync, | 
|  | thisLocal, | 
|  | boxedVariables, | 
|  | callMethod, | 
|  | signatureMethod, | 
|  | closureEntity, | 
|  | closureEntityVariable, | 
|  | closureClassEntity, | 
|  | localToFieldMap, | 
|  | typeVariableToFieldMap, | 
|  | thisLocalToFieldMap, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(ScopeInfoKind.closureRepresentationInfo); | 
|  | sink.begin(tag); | 
|  | sink.writeTreeNodes(_localsUsedInTryOrSync); | 
|  | sink.writeLocalOrNull(thisLocal); | 
|  | sink.writeTreeNodeMap(_boxedVariables, sink.writeMember); | 
|  | sink.writeMember(callMethod!); | 
|  | sink.writeMemberOrNull(signatureMethod); | 
|  | sink.writeLocalOrNull(_closureEntity); | 
|  | sink.writeTreeNodeOrNull(_closureEntityVariable); | 
|  | sink.writeClass(closureClassEntity); | 
|  | sink.writeTreeNodeMap(_variableToFieldMap, sink.writeMember); | 
|  | sink.writeTypeVariableMap(_typeVariableToFieldMap, sink.writeMember); | 
|  | sink.writeLocalMap(_localToFieldMap, sink.writeMember); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | bool hasFieldForLocal(Local local) => _localToFieldMap.containsKey(local); | 
|  |  | 
|  | void registerFieldForLocal(Local local, JField field) { | 
|  | assert(_fieldToLocalsMap == null); | 
|  | _localToFieldMap[local] = field; | 
|  | } | 
|  |  | 
|  | void registerFieldForVariable(ir.VariableDeclaration node, JField field) { | 
|  | assert(_fieldToLocalsMap == null); | 
|  | _variableToFieldMap[node] = field; | 
|  | } | 
|  |  | 
|  | bool hasFieldForTypeVariable(JTypeVariable typeVariable) => | 
|  | _typeVariableToFieldMap.containsKey(typeVariable); | 
|  |  | 
|  | void registerFieldForTypeVariable(JTypeVariable typeVariable, JField field) { | 
|  | assert(_fieldToLocalsMap == null); | 
|  | _typeVariableToFieldMap[typeVariable] = field; | 
|  | } | 
|  |  | 
|  | void registerFieldForBoxedVariable( | 
|  | ir.VariableDeclaration node, | 
|  | JField field, | 
|  | ) { | 
|  | assert(_boxedVariablesCache == null); | 
|  | _boxedVariables[node] = field as JContextField; | 
|  | } | 
|  |  | 
|  | void _ensureFieldToLocalsMap(KernelToLocalsMap localsMap) { | 
|  | if (_fieldToLocalsMap == null) { | 
|  | _fieldToLocalsMap = {}; | 
|  | _variableToFieldMap.forEach((ir.VariableDeclaration node, JField field) { | 
|  | _fieldToLocalsMap![field] = localsMap.getLocalVariable(node); | 
|  | }); | 
|  | _typeVariableToFieldMap.forEach(( | 
|  | TypeVariableEntity typeVariable, | 
|  | JField field, | 
|  | ) { | 
|  | _fieldToLocalsMap![field] = localsMap.getLocalTypeVariableEntity( | 
|  | typeVariable, | 
|  | ); | 
|  | }); | 
|  | _localToFieldMap.forEach((Local local, JField field) { | 
|  | _fieldToLocalsMap![field] = local; | 
|  | }); | 
|  | if (_fieldToLocalsMap!.isEmpty) { | 
|  | _fieldToLocalsMap = const {}; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | @override | 
|  | Local getLocalForField(KernelToLocalsMap localsMap, FieldEntity field) { | 
|  | _ensureFieldToLocalsMap(localsMap); | 
|  | return _fieldToLocalsMap![field]!; | 
|  | } | 
|  |  | 
|  | @override | 
|  | FieldEntity? get thisFieldEntity => _localToFieldMap[thisLocal]; | 
|  |  | 
|  | @override | 
|  | void forEachFreeVariable( | 
|  | KernelToLocalsMap localsMap, | 
|  | void Function(Local variable, JField field) f, | 
|  | ) { | 
|  | _ensureFieldToLocalsMap(localsMap); | 
|  | _ensureBoxedVariableCache(localsMap); | 
|  | _fieldToLocalsMap!.forEach((JField field, Local local) { | 
|  | f(local, field); | 
|  | }); | 
|  | _boxedVariablesCache!.forEach(f); | 
|  | } | 
|  |  | 
|  | @override | 
|  | bool get isClosure => true; | 
|  |  | 
|  | @override | 
|  | Local? getClosureEntity(KernelToLocalsMap localsMap) { | 
|  | return _closureEntityVariable != null | 
|  | ? localsMap.getLocalVariable(_closureEntityVariable) | 
|  | : _closureEntity; | 
|  | } | 
|  | } | 
|  |  | 
|  | abstract class ClosureRtiNeed { | 
|  | bool classNeedsTypeArguments(ClassEntity cls); | 
|  |  | 
|  | bool methodNeedsTypeArguments(FunctionEntity method); | 
|  |  | 
|  | bool methodNeedsSignature(MemberEntity method); | 
|  |  | 
|  | bool localFunctionNeedsTypeArguments(ir.LocalFunction node); | 
|  |  | 
|  | bool localFunctionNeedsSignature(ir.LocalFunction node); | 
|  |  | 
|  | bool selectorNeedsTypeArguments(Selector selector); | 
|  |  | 
|  | bool instantiationNeedsTypeArguments( | 
|  | FunctionType? functionType, | 
|  | int typeArgumentCount, | 
|  | ); | 
|  | } | 
|  |  | 
|  | /// A container for variables declared in a particular scope that are accessed | 
|  | /// elsewhere. | 
|  | // TODO(johnniwinther): Don't implement JClass. This isn't actually a class. | 
|  | class JContext extends JClass { | 
|  | /// Tag used for identifying serialized [JContext] objects in a debugging data | 
|  | /// stream. | 
|  | static const String tag = 'context'; | 
|  |  | 
|  | JContext(LibraryEntity library, String name) | 
|  | : super(library as JLibrary, name, isAbstract: false); | 
|  |  | 
|  | factory JContext.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | JLibrary library = source.readLibrary() as JLibrary; | 
|  | String name = source.readString(); | 
|  | source.end(tag); | 
|  | return JContext(library, name); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(JClassKind.context); | 
|  | sink.begin(tag); | 
|  | sink.writeLibrary(library); | 
|  | sink.writeString(name); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | @override | 
|  | bool get isClosure => false; | 
|  |  | 
|  | @override | 
|  | String toString() => '${jsElementPrefix}context($name)'; | 
|  | } | 
|  |  | 
|  | /// A variable that has been "boxed" to prevent name shadowing with the original | 
|  | /// variable and ensure that this variable is updated/read with the most recent | 
|  | /// value. | 
|  | class JContextField extends JField { | 
|  | /// Tag used for identifying serialized [JContextField] objects in a debugging | 
|  | /// data stream. | 
|  | static const String tag = 'context-field'; | 
|  |  | 
|  | final BoxLocal box; | 
|  |  | 
|  | JContextField(String name, this.box, {required bool isConst}) | 
|  | : super( | 
|  | box.container.library as JLibrary, | 
|  | box.container as JClass, | 
|  | Name(name, box.container.library.canonicalUri), | 
|  | isStatic: false, | 
|  | isAssignable: true, | 
|  | isConst: isConst, | 
|  | ); | 
|  |  | 
|  | factory JContextField.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | String name = source.readString(); | 
|  | final enclosingClass = source.readClass() as JClass; | 
|  | bool isConst = source.readBool(); | 
|  | source.end(tag); | 
|  | return JContextField(name, BoxLocal(enclosingClass), isConst: isConst); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(JMemberKind.contextField); | 
|  | sink.begin(tag); | 
|  | sink.writeString(name); | 
|  | sink.writeClass(enclosingClass!); | 
|  | sink.writeBool(isConst); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | // TODO(johnniwinther): Remove these anomalies. Maybe by separating the | 
|  | // J-entities from the K-entities. | 
|  | @override | 
|  | bool get isInstanceMember => false; | 
|  |  | 
|  | @override | 
|  | bool get isTopLevel => false; | 
|  |  | 
|  | @override | 
|  | bool get isStatic => false; | 
|  | } | 
|  |  | 
|  | class JClosureClass extends JClass { | 
|  | /// Tag used for identifying serialized [JClosureClass] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'closure-class'; | 
|  |  | 
|  | JClosureClass(super.library, super.name) : super(isAbstract: false); | 
|  |  | 
|  | factory JClosureClass.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | JLibrary library = source.readLibrary() as JLibrary; | 
|  | String name = source.readString(); | 
|  | source.end(tag); | 
|  | return JClosureClass(library, name); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(JClassKind.closure); | 
|  | sink.begin(tag); | 
|  | sink.writeLibrary(library); | 
|  | sink.writeString(name); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | @override | 
|  | bool get isClosure => true; | 
|  |  | 
|  | @override | 
|  | String toString() => '${jsElementPrefix}closure_class($name)'; | 
|  | } | 
|  |  | 
|  | class AnonymousClosureLocal implements Local { | 
|  | final JClosureClass closureClass; | 
|  |  | 
|  | AnonymousClosureLocal(this.closureClass); | 
|  |  | 
|  | @override | 
|  | String get name => ''; | 
|  |  | 
|  | @override | 
|  | int get hashCode => closureClass.hashCode * 13; | 
|  |  | 
|  | @override | 
|  | bool operator ==(other) { | 
|  | if (identical(this, other)) return true; | 
|  | if (other is! AnonymousClosureLocal) return false; | 
|  | return closureClass == other.closureClass; | 
|  | } | 
|  |  | 
|  | @override | 
|  | String toString() => | 
|  | '${jsElementPrefix}anonymous_closure_local(${closureClass.name})'; | 
|  | } | 
|  |  | 
|  | class JClosureField extends JField implements PrivatelyNamedJSEntity { | 
|  | /// Tag used for identifying serialized [JClosureClass] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'closure-field'; | 
|  |  | 
|  | @override | 
|  | final String declaredName; | 
|  |  | 
|  | JClosureField( | 
|  | String name, | 
|  | JsClosureClassInfo containingClass, | 
|  | String declaredName, { | 
|  | required bool isConst, | 
|  | required bool isAssignable, | 
|  | }) : this.internal( | 
|  | containingClass.closureClassEntity.library, | 
|  | containingClass.closureClassEntity as JClosureClass, | 
|  | Name(name, containingClass.closureClassEntity.library.canonicalUri), | 
|  | declaredName, | 
|  | isAssignable: isAssignable, | 
|  | isConst: isConst, | 
|  | ); | 
|  |  | 
|  | JClosureField.internal( | 
|  | super.library, | 
|  | JClosureClass super.enclosingClass, | 
|  | super.memberName, | 
|  | this.declaredName, { | 
|  | required super.isConst, | 
|  | required super.isAssignable, | 
|  | }) : super(isStatic: false); | 
|  |  | 
|  | factory JClosureField.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | final cls = source.readClass() as JClosureClass; | 
|  | String name = source.readString(); | 
|  | String declaredName = source.readString(); | 
|  | bool isConst = source.readBool(); | 
|  | bool isAssignable = source.readBool(); | 
|  | source.end(tag); | 
|  | return JClosureField.internal( | 
|  | cls.library, | 
|  | cls, | 
|  | Name(name, cls.library.canonicalUri), | 
|  | declaredName, | 
|  | isAssignable: isAssignable, | 
|  | isConst: isConst, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(JMemberKind.closureField); | 
|  | sink.begin(tag); | 
|  | sink.writeClass(enclosingClass!); | 
|  | sink.writeString(name); | 
|  | sink.writeString(declaredName); | 
|  | sink.writeBool(isConst); | 
|  | sink.writeBool(isAssignable); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | @override | 
|  | Entity get rootOfScope => enclosingClass!; | 
|  | } | 
|  |  | 
|  | class ContextClassData implements JClassData { | 
|  | /// Tag used for identifying serialized [ContextClassData] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'context-class-data'; | 
|  |  | 
|  | @override | 
|  | final ClassDefinition definition; | 
|  |  | 
|  | @override | 
|  | final InterfaceType? thisType; | 
|  |  | 
|  | @override | 
|  | final OrderedTypeSet orderedTypeSet; | 
|  |  | 
|  | @override | 
|  | final InterfaceType? supertype; | 
|  |  | 
|  | ContextClassData( | 
|  | this.definition, | 
|  | this.thisType, | 
|  | this.supertype, | 
|  | this.orderedTypeSet, | 
|  | ); | 
|  |  | 
|  | factory ContextClassData.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | ClassDefinition definition = ClassDefinition.readFromDataSource(source); | 
|  | InterfaceType thisType = source.readDartType() as InterfaceType; | 
|  | InterfaceType supertype = source.readDartType() as InterfaceType; | 
|  | OrderedTypeSet orderedTypeSet = OrderedTypeSet.readFromDataSource(source); | 
|  | source.end(tag); | 
|  | return ContextClassData(definition, thisType, supertype, orderedTypeSet); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(JClassDataKind.context); | 
|  | sink.begin(tag); | 
|  | definition.writeToDataSink(sink); | 
|  | sink.writeDartType(thisType!); | 
|  | sink.writeDartType(supertype!); | 
|  | orderedTypeSet.writeToDataSink(sink); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | @override | 
|  | bool get isMixinApplication => false; | 
|  |  | 
|  | @override | 
|  | bool get isEnumClass => false; | 
|  |  | 
|  | @override | 
|  | FunctionType? get callType => null; | 
|  |  | 
|  | @override | 
|  | List<InterfaceType> get interfaces => const <InterfaceType>[]; | 
|  |  | 
|  | @override | 
|  | InterfaceType? get mixedInType => null; | 
|  |  | 
|  | @override | 
|  | InterfaceType? get jsInteropType => thisType; | 
|  |  | 
|  | @override | 
|  | InterfaceType? get rawType => thisType; | 
|  |  | 
|  | @override | 
|  | InterfaceType? get instantiationToBounds => thisType; | 
|  |  | 
|  | @override | 
|  | List<Variance> getVariances() => []; | 
|  | } | 
|  |  | 
|  | class ClosureClassData extends ContextClassData { | 
|  | /// Tag used for identifying serialized [ClosureClassData] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'closure-class-data'; | 
|  |  | 
|  | @override | 
|  | FunctionType? callType; | 
|  |  | 
|  | ClosureClassData( | 
|  | super.definition, | 
|  | super.thisType, | 
|  | super.supertype, | 
|  | super.orderedTypeSet, | 
|  | ); | 
|  |  | 
|  | factory ClosureClassData.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | ClassDefinition definition = ClassDefinition.readFromDataSource(source); | 
|  | InterfaceType thisType = source.readDartType() as InterfaceType; | 
|  | InterfaceType supertype = source.readDartType() as InterfaceType; | 
|  | OrderedTypeSet orderedTypeSet = OrderedTypeSet.readFromDataSource(source); | 
|  | FunctionType callType = source.readDartType() as FunctionType; | 
|  | source.end(tag); | 
|  | return ClosureClassData(definition, thisType, supertype, orderedTypeSet) | 
|  | ..callType = callType; | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(JClassDataKind.closure); | 
|  | sink.begin(tag); | 
|  | definition.writeToDataSink(sink); | 
|  | sink.writeDartType(thisType!); | 
|  | sink.writeDartType(supertype!); | 
|  | orderedTypeSet.writeToDataSink(sink); | 
|  | sink.writeDartType(callType!); | 
|  | sink.end(tag); | 
|  | } | 
|  | } | 
|  |  | 
|  | abstract class ClosureMemberData implements JMemberData { | 
|  | @override | 
|  | final MemberDefinition definition; | 
|  | final InterfaceType? memberThisType; | 
|  |  | 
|  | ClosureMemberData(this.definition, this.memberThisType); | 
|  |  | 
|  | @override | 
|  | InterfaceType? getMemberThisType(covariant JsToElementMap elementMap) { | 
|  | return memberThisType; | 
|  | } | 
|  | } | 
|  |  | 
|  | class ClosureFunctionData extends ClosureMemberData | 
|  | with FunctionDataTypeVariablesMixin, FunctionDataForEachParameterMixin | 
|  | implements FunctionData { | 
|  | /// Tag used for identifying serialized [ClosureFunctionData] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'closure-function-data'; | 
|  |  | 
|  | final FunctionType functionType; | 
|  | @override | 
|  | ir.FunctionNode get functionNode => _functionNode.loaded(); | 
|  | final Deferrable<ir.FunctionNode> _functionNode; | 
|  | @override | 
|  | final ClassTypeVariableAccess classTypeVariableAccess; | 
|  |  | 
|  | ClosureFunctionData( | 
|  | super.definition, | 
|  | super.memberThisType, | 
|  | this.functionType, | 
|  | ir.FunctionNode functionNode, | 
|  | this.classTypeVariableAccess, | 
|  | ) : _functionNode = Deferrable.eager(functionNode); | 
|  |  | 
|  | ClosureFunctionData._deserialized( | 
|  | super.definition, | 
|  | super.memberThisType, | 
|  | this.functionType, | 
|  | this._functionNode, | 
|  | this.classTypeVariableAccess, | 
|  | ); | 
|  |  | 
|  | static ir.FunctionNode _readFunctionNode(DataSourceReader source) { | 
|  | return source.readTreeNode() as ir.FunctionNode; | 
|  | } | 
|  |  | 
|  | factory ClosureFunctionData.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | ClosureMemberDefinition definition = | 
|  | MemberDefinition.readFromDataSource(source) as ClosureMemberDefinition; | 
|  | InterfaceType? memberThisType = | 
|  | source.readDartTypeOrNull() as InterfaceType?; | 
|  | FunctionType functionType = source.readDartType() as FunctionType; | 
|  | Deferrable<ir.FunctionNode> functionNode = source.readDeferrable( | 
|  | _readFunctionNode, | 
|  | ); | 
|  | ClassTypeVariableAccess classTypeVariableAccess = source.readEnum( | 
|  | ClassTypeVariableAccess.values, | 
|  | ); | 
|  | source.end(tag); | 
|  | return ClosureFunctionData._deserialized( | 
|  | definition, | 
|  | memberThisType, | 
|  | functionType, | 
|  | functionNode, | 
|  | classTypeVariableAccess, | 
|  | ); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(JMemberDataKind.closureFunction); | 
|  | sink.begin(tag); | 
|  | definition.writeToDataSink(sink); | 
|  | sink.writeDartTypeOrNull(memberThisType); | 
|  | sink.writeDartType(functionType); | 
|  | sink.writeDeferrable(() => sink.writeTreeNode(functionNode)); | 
|  | sink.writeEnum(classTypeVariableAccess); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | @override | 
|  | FunctionType getFunctionType(IrToElementMap elementMap) { | 
|  | return functionType; | 
|  | } | 
|  | } | 
|  |  | 
|  | class ClosureFieldData extends ClosureMemberData implements JFieldData { | 
|  | /// Tag used for identifying serialized [ClosureFieldData] objects in a | 
|  | /// debugging data stream. | 
|  | static const String tag = 'closure-field-data'; | 
|  |  | 
|  | DartType? _type; | 
|  |  | 
|  | ClosureFieldData(super.definition, super.memberThisType); | 
|  |  | 
|  | factory ClosureFieldData.readFromDataSource(DataSourceReader source) { | 
|  | source.begin(tag); | 
|  | MemberDefinition definition = MemberDefinition.readFromDataSource(source); | 
|  | InterfaceType? memberThisType = | 
|  | source.readDartTypeOrNull() as InterfaceType?; | 
|  | source.end(tag); | 
|  | return ClosureFieldData(definition, memberThisType); | 
|  | } | 
|  |  | 
|  | @override | 
|  | void writeToDataSink(DataSinkWriter sink) { | 
|  | sink.writeEnum(JMemberDataKind.closureField); | 
|  | sink.begin(tag); | 
|  | definition.writeToDataSink(sink); | 
|  | sink.writeDartTypeOrNull(memberThisType); | 
|  | sink.end(tag); | 
|  | } | 
|  |  | 
|  | @override | 
|  | DartType getFieldType(IrToElementMap elementMap) { | 
|  | if (_type != null) return _type!; | 
|  | ir.Node sourceNode = definition.node; | 
|  | ir.DartType type; | 
|  | if (sourceNode is ir.Class) { | 
|  | type = sourceNode.getThisType( | 
|  | elementMap.coreTypes, | 
|  | sourceNode.enclosingLibrary.nonNullable, | 
|  | ); | 
|  | } else if (sourceNode is ir.VariableDeclaration) { | 
|  | type = sourceNode.type; | 
|  | } else if (sourceNode is ir.Field) { | 
|  | type = sourceNode.type; | 
|  | } else if (sourceNode is ir.TypeLiteral) { | 
|  | type = sourceNode.type; | 
|  | } else if (sourceNode is ir.Typedef) { | 
|  | type = sourceNode.type!; | 
|  | } else if (sourceNode is ir.TypeParameter) { | 
|  | type = sourceNode.bound; | 
|  | } else { | 
|  | failedAt( | 
|  | definition.location, | 
|  | 'Unexpected node type $sourceNode in ' | 
|  | 'ClosureFieldData.getFieldType', | 
|  | ); | 
|  | } | 
|  | return _type = elementMap.getDartType(type); | 
|  | } | 
|  |  | 
|  | @override | 
|  | bool get isCovariantByDeclaration => false; | 
|  |  | 
|  | @override | 
|  | ClassTypeVariableAccess get classTypeVariableAccess => | 
|  | ClassTypeVariableAccess.none; | 
|  | } |