blob: 2959ff305e9b2e45178306aa19341f1d7bffcdb8 [file] [log] [blame]
// Copyright (c) 2022, 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:kernel/ast.dart' as ir;
import '../closure_migrated.dart';
import '../common.dart';
import '../elements/entities.dart';
import '../elements/names.dart' show Name;
import '../elements/types.dart';
import '../ir/element_map.dart';
import '../ir/static_type_cache.dart';
import '../js_model/class_type_variable_access.dart';
import '../ordered_typeset.dart';
import '../serialization/deferrable.dart';
import '../serialization/serialization.dart';
import 'element_map_interfaces.dart';
import 'element_map_migrated.dart';
import 'elements.dart';
import 'env.dart';
/// 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!;
}
abstract class JsClosureClassInfo {
JClass get closureClassEntity;
Local? get thisLocal;
JFunction? get callMethod;
void set callMethod(JFunction? value);
JSignatureMethod? get signatureMethod;
void set signatureMethod(JSignatureMethod? value);
bool hasFieldForLocal(Local local);
bool hasFieldForTypeVariable(JTypeVariable typeVariable);
void registerFieldForTypeVariable(JTypeVariable typeVariable, JField field);
void registerFieldForLocal(Local local, JField field);
void registerFieldForVariable(ir.VariableDeclaration node, JField field);
void registerFieldForBoxedVariable(ir.VariableDeclaration node, JField field);
}
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
StaticTypeCache get staticTypes {
// The cached types are stored in the data for enclosing member.
throw UnsupportedError("ClosureMemberData.staticTypes");
}
@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);
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(() => source.readTreeNode() as ir.FunctionNode);
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
late final ir.Member memberContext = (() {
ir.TreeNode parent = functionNode;
while (parent is! ir.Member) {
parent = parent.parent!;
}
return parent;
})();
@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
ClassTypeVariableAccess get classTypeVariableAccess =>
ClassTypeVariableAccess.none;
}