blob: 4afe45a045b5deabcce4ab2686dfdf577dee5e9e [file] [log] [blame]
// 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.
library dart2js.js_model.elements;
import '../common/names.dart' show Names;
import '../elements/entities.dart';
import '../elements/indexed.dart';
import '../elements/names.dart';
import '../elements/types.dart';
import '../serialization/serialization.dart';
import '../universe/class_set.dart' show ClassHierarchyNodesMapKey;
import 'closure.dart';
const String jsElementPrefix = 'j:';
class JLibrary extends IndexedLibrary {
/// Tag used for identifying serialized [JLibrary] objects in a
/// debugging data stream.
static const String tag = 'library';
final String name;
final Uri canonicalUri;
JLibrary(this.name, this.canonicalUri);
/// Deserializes a [JLibrary] object from [source].
factory JLibrary.readFromDataSource(DataSource source) {
source.begin(tag);
String name = source.readString();
Uri canonicalUri = source.readUri();
source.end(tag);
return new JLibrary(name, canonicalUri);
}
/// Serializes this [JLibrary] to [sink].
void writeToDataSink(DataSink sink) {
sink.begin(tag);
sink.writeString(name);
sink.writeUri(canonicalUri);
sink.end(tag);
}
String toString() => '${jsElementPrefix}library($name)';
}
/// Enum used for identifying [JClass] subclasses in serialization.
enum JClassKind { node, closure, record }
class JClass extends IndexedClass with ClassHierarchyNodesMapKey {
/// Tag used for identifying serialized [JClass] objects in a
/// debugging data stream.
static const String tag = 'class';
final JLibrary library;
final String name;
final bool isAbstract;
JClass(this.library, this.name, {this.isAbstract});
/// Deserializes a [JClass] object from [source].
factory JClass.readFromDataSource(DataSource source) {
JClassKind kind = source.readEnum(JClassKind.values);
switch (kind) {
case JClassKind.node:
source.begin(tag);
JLibrary library = source.readLibrary();
String name = source.readString();
bool isAbstract = source.readBool();
source.end(tag);
return new JClass(library, name, isAbstract: isAbstract);
case JClassKind.closure:
return new JClosureClass.readFromDataSource(source);
case JClassKind.record:
return new JRecord.readFromDataSource(source);
}
throw new UnsupportedError("Unexpected ClassKind $kind");
}
/// Serializes this [JClass] to [sink].
void writeToDataSink(DataSink sink) {
sink.writeEnum(JClassKind.node);
sink.begin(tag);
sink.writeLibrary(library);
sink.writeString(name);
sink.writeBool(isAbstract);
sink.end(tag);
}
@override
bool get isClosure => false;
String toString() => '${jsElementPrefix}class($name)';
}
class JTypedef extends IndexedTypedef {
/// Tag used for identifying serialized [JTypedef] objects in a
/// debugging data stream.
static const String tag = 'typedef';
final JLibrary library;
final String name;
JTypedef(this.library, this.name);
/// Deserializes a [JTypedef] object from [source].
factory JTypedef.readFromDataSource(DataSource source) {
source.begin(tag);
JLibrary library = source.readLibrary();
String name = source.readString();
source.end(tag);
return new JTypedef(library, name);
}
/// Serializes this [JTypedef] to [sink].
void writeToDataSink(DataSink sink) {
sink.begin(tag);
sink.writeLibrary(library);
sink.writeString(name);
sink.end(tag);
}
String toString() => '${jsElementPrefix}typedef($name)';
}
/// Enum used for identifying [JMember] subclasses in serialization.
enum JMemberKind {
generativeConstructor,
factoryConstructor,
constructorBody,
field,
getter,
setter,
method,
closureField,
closureCallMethod,
generatorBody,
signatureMethod,
recordField,
}
abstract class JMember extends IndexedMember {
final JLibrary library;
final JClass enclosingClass;
final Name _name;
final bool _isStatic;
JMember(this.library, this.enclosingClass, this._name, {bool isStatic: false})
: _isStatic = isStatic;
/// Deserializes a [JMember] object from [source].
factory JMember.readFromDataSource(DataSource source) {
JMemberKind kind = source.readEnum(JMemberKind.values);
switch (kind) {
case JMemberKind.generativeConstructor:
return new JGenerativeConstructor.readFromDataSource(source);
case JMemberKind.factoryConstructor:
return new JFactoryConstructor.readFromDataSource(source);
case JMemberKind.constructorBody:
return new JConstructorBody.readFromDataSource(source);
case JMemberKind.field:
return new JField.readFromDataSource(source);
case JMemberKind.getter:
return new JGetter.readFromDataSource(source);
case JMemberKind.setter:
return new JSetter.readFromDataSource(source);
case JMemberKind.method:
return new JMethod.readFromDataSource(source);
case JMemberKind.closureField:
return new JClosureField.readFromDataSource(source);
case JMemberKind.closureCallMethod:
return new JClosureCallMethod.readFromDataSource(source);
case JMemberKind.generatorBody:
return new JGeneratorBody.readFromDataSource(source);
case JMemberKind.signatureMethod:
return new JSignatureMethod.readFromDataSource(source);
case JMemberKind.recordField:
return new JRecordField.readFromDataSource(source);
}
throw new UnsupportedError("Unexpected JMemberKind $kind");
}
/// Serializes this [JMember] to [sink].
void writeToDataSink(DataSink sink);
String get name => _name.text;
Name get memberName => _name;
@override
bool get isAssignable => false;
@override
bool get isConst => false;
@override
bool get isAbstract => false;
@override
bool get isSetter => false;
@override
bool get isGetter => false;
@override
bool get isFunction => false;
@override
bool get isField => false;
@override
bool get isConstructor => false;
@override
bool get isInstanceMember => enclosingClass != null && !_isStatic;
@override
bool get isStatic => enclosingClass != null && _isStatic;
@override
bool get isTopLevel => enclosingClass == null;
String get _kind;
String toString() => '${jsElementPrefix}$_kind'
'(${enclosingClass != null ? '${enclosingClass.name}.' : ''}$name)';
}
abstract class JFunction extends JMember
implements FunctionEntity, IndexedFunction {
final ParameterStructure parameterStructure;
final bool isExternal;
final AsyncMarker asyncMarker;
JFunction(JLibrary library, JClass enclosingClass, Name name,
this.parameterStructure, this.asyncMarker,
{bool isStatic: false, this.isExternal: false})
: super(library, enclosingClass, name, isStatic: isStatic);
}
abstract class JConstructor extends JFunction
implements ConstructorEntity, IndexedConstructor {
final bool isConst;
JConstructor(
JClass enclosingClass, Name name, ParameterStructure parameterStructure,
{bool isExternal, this.isConst})
: super(enclosingClass.library, enclosingClass, name, parameterStructure,
AsyncMarker.SYNC,
isExternal: isExternal);
@override
bool get isConstructor => true;
@override
bool get isInstanceMember => false;
@override
bool get isStatic => false;
@override
bool get isTopLevel => false;
@override
bool get isFromEnvironmentConstructor => false;
String get _kind => 'constructor';
}
class JGenerativeConstructor extends JConstructor {
/// Tag used for identifying serialized [JGenerativeConstructor] objects in a
/// debugging data stream.
static const String tag = 'generative-constructor';
JGenerativeConstructor(
JClass enclosingClass, Name name, ParameterStructure parameterStructure,
{bool isExternal, bool isConst})
: super(enclosingClass, name, parameterStructure,
isExternal: isExternal, isConst: isConst);
factory JGenerativeConstructor.readFromDataSource(DataSource source) {
source.begin(tag);
JClass enclosingClass = source.readClass();
String name = source.readString();
ParameterStructure parameterStructure =
new ParameterStructure.readFromDataSource(source);
bool isExternal = source.readBool();
bool isConst = source.readBool();
source.end(tag);
return new JGenerativeConstructor(enclosingClass,
new Name(name, enclosingClass.library), parameterStructure,
isExternal: isExternal, isConst: isConst);
}
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.generativeConstructor);
sink.begin(tag);
sink.writeClass(enclosingClass);
sink.writeString(name);
parameterStructure.writeToDataSink(sink);
sink.writeBool(isExternal);
sink.writeBool(isConst);
sink.end(tag);
}
@override
bool get isFactoryConstructor => false;
@override
bool get isGenerativeConstructor => true;
}
class JFactoryConstructor extends JConstructor {
/// Tag used for identifying serialized [JFactoryConstructor] objects in a
/// debugging data stream.
static const String tag = 'factory-constructor';
@override
final bool isFromEnvironmentConstructor;
JFactoryConstructor(
JClass enclosingClass, Name name, ParameterStructure parameterStructure,
{bool isExternal, bool isConst, this.isFromEnvironmentConstructor})
: super(enclosingClass, name, parameterStructure,
isExternal: isExternal, isConst: isConst);
factory JFactoryConstructor.readFromDataSource(DataSource source) {
source.begin(tag);
JClass enclosingClass = source.readClass();
String name = source.readString();
ParameterStructure parameterStructure =
new ParameterStructure.readFromDataSource(source);
bool isExternal = source.readBool();
bool isConst = source.readBool();
bool isFromEnvironmentConstructor = source.readBool();
source.end(tag);
return new JFactoryConstructor(enclosingClass,
new Name(name, enclosingClass.library), parameterStructure,
isExternal: isExternal,
isConst: isConst,
isFromEnvironmentConstructor: isFromEnvironmentConstructor);
}
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.factoryConstructor);
sink.begin(tag);
sink.writeClass(enclosingClass);
sink.writeString(name);
parameterStructure.writeToDataSink(sink);
sink.writeBool(isExternal);
sink.writeBool(isConst);
sink.writeBool(isFromEnvironmentConstructor);
sink.end(tag);
}
@override
bool get isFactoryConstructor => true;
@override
bool get isGenerativeConstructor => false;
}
class JConstructorBody extends JFunction implements ConstructorBodyEntity {
/// Tag used for identifying serialized [JConstructorBody] objects in a
/// debugging data stream.
static const String tag = 'constructor-body';
final JConstructor constructor;
JConstructorBody(this.constructor, ParameterStructure parameterStructure)
: super(constructor.library, constructor.enclosingClass,
constructor.memberName, parameterStructure, AsyncMarker.SYNC,
isStatic: false, isExternal: false);
factory JConstructorBody.readFromDataSource(DataSource source) {
source.begin(tag);
JConstructor constructor = source.readMember();
ParameterStructure parameterStructure =
new ParameterStructure.readFromDataSource(source);
source.end(tag);
return new JConstructorBody(constructor, parameterStructure);
}
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.constructorBody);
sink.begin(tag);
sink.writeMember(constructor);
parameterStructure.writeToDataSink(sink);
sink.end(tag);
}
String get _kind => 'constructor_body';
}
class JMethod extends JFunction {
/// Tag used for identifying serialized [JMethod] objects in a
/// debugging data stream.
static const String tag = 'method';
final bool isAbstract;
JMethod(JLibrary library, JClass enclosingClass, Name name,
ParameterStructure parameterStructure, AsyncMarker asyncMarker,
{bool isStatic, bool isExternal, this.isAbstract})
: super(library, enclosingClass, name, parameterStructure, asyncMarker,
isStatic: isStatic, isExternal: isExternal);
factory JMethod.readFromDataSource(DataSource source) {
source.begin(tag);
MemberContextKind kind = source.readEnum(MemberContextKind.values);
JLibrary library;
JClass enclosingClass;
switch (kind) {
case MemberContextKind.library:
library = source.readLibrary();
break;
case MemberContextKind.cls:
enclosingClass = source.readClass();
library = enclosingClass.library;
break;
}
String name = source.readString();
ParameterStructure parameterStructure =
new ParameterStructure.readFromDataSource(source);
AsyncMarker asyncMarker = source.readEnum(AsyncMarker.values);
bool isStatic = source.readBool();
bool isExternal = source.readBool();
bool isAbstract = source.readBool();
source.end(tag);
return new JMethod(library, enclosingClass, new Name(name, library),
parameterStructure, asyncMarker,
isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract);
}
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.method);
sink.begin(tag);
if (enclosingClass != null) {
sink.writeEnum(MemberContextKind.cls);
sink.writeClass(enclosingClass);
} else {
sink.writeEnum(MemberContextKind.library);
sink.writeLibrary(library);
}
sink.writeString(name);
parameterStructure.writeToDataSink(sink);
sink.writeEnum(asyncMarker);
sink.writeBool(isStatic);
sink.writeBool(isExternal);
sink.writeBool(isAbstract);
sink.end(tag);
}
@override
bool get isFunction => true;
String get _kind => 'method';
}
class JGeneratorBody extends JFunction {
/// Tag used for identifying serialized [JGeneratorBody] objects in a
/// debugging data stream.
static const String tag = 'generator-body';
final JFunction function;
final DartType elementType;
final int hashCode;
JGeneratorBody(this.function, this.elementType)
: hashCode = function.hashCode + 1, // Hack stabilize sort order.
super(function.library, function.enclosingClass, function.memberName,
function.parameterStructure, function.asyncMarker,
isStatic: function.isStatic, isExternal: false);
factory JGeneratorBody.readFromDataSource(DataSource source) {
source.begin(tag);
JFunction function = source.readMember();
DartType elementType = source.readDartType();
source.end(tag);
return new JGeneratorBody(function, elementType);
}
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.generatorBody);
sink.begin(tag);
sink.writeMember(function);
sink.writeDartType(elementType);
sink.end(tag);
}
String get _kind => 'generator_body';
}
class JGetter extends JFunction {
/// Tag used for identifying serialized [JGetter] objects in a
/// debugging data stream.
static const String tag = 'getter';
final bool isAbstract;
JGetter(JLibrary library, JClass enclosingClass, Name name,
AsyncMarker asyncMarker,
{bool isStatic, bool isExternal, this.isAbstract})
: super(library, enclosingClass, name, const ParameterStructure.getter(),
asyncMarker,
isStatic: isStatic, isExternal: isExternal);
factory JGetter.readFromDataSource(DataSource source) {
source.begin(tag);
MemberContextKind kind = source.readEnum(MemberContextKind.values);
JLibrary library;
JClass enclosingClass;
switch (kind) {
case MemberContextKind.library:
library = source.readLibrary();
break;
case MemberContextKind.cls:
enclosingClass = source.readClass();
library = enclosingClass.library;
break;
}
String name = source.readString();
AsyncMarker asyncMarker = source.readEnum(AsyncMarker.values);
bool isStatic = source.readBool();
bool isExternal = source.readBool();
bool isAbstract = source.readBool();
source.end(tag);
return new JGetter(
library, enclosingClass, new Name(name, library), asyncMarker,
isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract);
}
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.getter);
sink.begin(tag);
if (enclosingClass != null) {
sink.writeEnum(MemberContextKind.cls);
sink.writeClass(enclosingClass);
} else {
sink.writeEnum(MemberContextKind.library);
sink.writeLibrary(library);
}
sink.writeString(name);
sink.writeEnum(asyncMarker);
sink.writeBool(isStatic);
sink.writeBool(isExternal);
sink.writeBool(isAbstract);
sink.end(tag);
}
@override
bool get isGetter => true;
String get _kind => 'getter';
}
class JSetter extends JFunction {
/// Tag used for identifying serialized [JSetter] objects in a
/// debugging data stream.
static const String tag = 'setter';
final bool isAbstract;
JSetter(JLibrary library, JClass enclosingClass, Name name,
{bool isStatic, bool isExternal, this.isAbstract})
: super(library, enclosingClass, name, const ParameterStructure.setter(),
AsyncMarker.SYNC,
isStatic: isStatic, isExternal: isExternal);
factory JSetter.readFromDataSource(DataSource source) {
source.begin(tag);
MemberContextKind kind = source.readEnum(MemberContextKind.values);
JLibrary library;
JClass enclosingClass;
switch (kind) {
case MemberContextKind.library:
library = source.readLibrary();
break;
case MemberContextKind.cls:
enclosingClass = source.readClass();
library = enclosingClass.library;
break;
}
String name = source.readString();
bool isStatic = source.readBool();
bool isExternal = source.readBool();
bool isAbstract = source.readBool();
source.end(tag);
return new JSetter(
library, enclosingClass, new Name(name, library, isSetter: true),
isStatic: isStatic, isExternal: isExternal, isAbstract: isAbstract);
}
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.setter);
sink.begin(tag);
if (enclosingClass != null) {
sink.writeEnum(MemberContextKind.cls);
sink.writeClass(enclosingClass);
} else {
sink.writeEnum(MemberContextKind.library);
sink.writeLibrary(library);
}
sink.writeString(name);
sink.writeBool(isStatic);
sink.writeBool(isExternal);
sink.writeBool(isAbstract);
sink.end(tag);
}
@override
bool get isAssignable => true;
@override
bool get isSetter => true;
String get _kind => 'setter';
}
class JField extends JMember implements FieldEntity, IndexedField {
/// Tag used for identifying serialized [JField] objects in a
/// debugging data stream.
static const String tag = 'field';
final bool isAssignable;
final bool isConst;
JField(JLibrary library, JClass enclosingClass, Name name,
{bool isStatic, this.isAssignable, this.isConst})
: super(library, enclosingClass, name, isStatic: isStatic);
factory JField.readFromDataSource(DataSource source) {
source.begin(tag);
MemberContextKind kind = source.readEnum(MemberContextKind.values);
JLibrary library;
JClass enclosingClass;
switch (kind) {
case MemberContextKind.library:
library = source.readLibrary();
break;
case MemberContextKind.cls:
enclosingClass = source.readClass();
library = enclosingClass.library;
break;
}
String name = source.readString();
bool isStatic = source.readBool();
bool isAssignable = source.readBool();
bool isConst = source.readBool();
source.end(tag);
return new JField(library, enclosingClass, new Name(name, library),
isStatic: isStatic, isAssignable: isAssignable, isConst: isConst);
}
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.field);
sink.begin(tag);
if (enclosingClass != null) {
sink.writeEnum(MemberContextKind.cls);
sink.writeClass(enclosingClass);
} else {
sink.writeEnum(MemberContextKind.library);
sink.writeLibrary(library);
}
sink.writeString(name);
sink.writeBool(isStatic);
sink.writeBool(isAssignable);
sink.writeBool(isConst);
sink.end(tag);
}
@override
bool get isField => true;
String get _kind => 'field';
}
class JClosureCallMethod extends JMethod {
/// Tag used for identifying serialized [JClosureCallMethod] objects in a
/// debugging data stream.
static const String tag = 'closure-call-method';
JClosureCallMethod(ClassEntity enclosingClass,
ParameterStructure parameterStructure, AsyncMarker asyncMarker)
: super(enclosingClass.library, enclosingClass, Names.call,
parameterStructure, asyncMarker,
isStatic: false, isExternal: false, isAbstract: false);
factory JClosureCallMethod.readFromDataSource(DataSource source) {
source.begin(tag);
JClass enclosingClass = source.readClass();
ParameterStructure parameterStructure =
new ParameterStructure.readFromDataSource(source);
AsyncMarker asyncMarker = source.readEnum(AsyncMarker.values);
source.end(tag);
return new JClosureCallMethod(
enclosingClass, parameterStructure, asyncMarker);
}
@override
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.closureCallMethod);
sink.begin(tag);
sink.writeClass(enclosingClass);
parameterStructure.writeToDataSink(sink);
sink.writeEnum(asyncMarker);
sink.end(tag);
}
String get _kind => 'closure_call';
}
/// A method that returns the signature of the Dart closure/tearoff that this
/// method's parent class is representing.
class JSignatureMethod extends JMethod {
/// Tag used for identifying serialized [JSignatureMethod] objects in a
/// debugging data stream.
static const String tag = 'signature-method';
JSignatureMethod(ClassEntity enclosingClass)
: super(enclosingClass.library, enclosingClass, Names.signature,
const ParameterStructure(0, 0, const [], 0), AsyncMarker.SYNC,
isStatic: false, isExternal: false, isAbstract: false);
factory JSignatureMethod.readFromDataSource(DataSource source) {
source.begin(tag);
JClass cls = source.readClass();
source.end(tag);
return new JSignatureMethod(cls);
}
void writeToDataSink(DataSink sink) {
sink.writeEnum(JMemberKind.signatureMethod);
sink.begin(tag);
sink.writeClass(enclosingClass);
sink.end(tag);
}
String get _kind => 'signature';
}
/// Enum used for identifying [JTypeVariable] variants in serialization.
enum JTypeVariableKind { cls, member, typedef, local }
class JTypeVariable extends IndexedTypeVariable {
/// Tag used for identifying serialized [JTypeVariable] objects in a
/// debugging data stream.
static const String tag = 'type-variable';
final Entity typeDeclaration;
final String name;
final int index;
JTypeVariable(this.typeDeclaration, this.name, this.index);
/// Deserializes a [JTypeVariable] object from [source].
factory JTypeVariable.readFromDataSource(DataSource source) {
source.begin(tag);
JTypeVariableKind kind = source.readEnum(JTypeVariableKind.values);
Entity typeDeclaration;
switch (kind) {
case JTypeVariableKind.cls:
typeDeclaration = source.readClass();
break;
case JTypeVariableKind.member:
typeDeclaration = source.readMember();
break;
case JTypeVariableKind.typedef:
typeDeclaration = source.readTypedef();
break;
case JTypeVariableKind.local:
// Type variables declared by local functions don't point to their
// declaration, since the corresponding closure call methods is created
// after the type variable.
// TODO(johnniwinther): Fix this.
break;
}
String name = source.readString();
int index = source.readInt();
source.end(tag);
return new JTypeVariable(typeDeclaration, name, index);
}
/// Serializes this [JTypeVariable] to [sink].
void writeToDataSink(DataSink sink) {
sink.begin(tag);
if (typeDeclaration is IndexedClass) {
IndexedClass cls = typeDeclaration;
sink.writeEnum(JTypeVariableKind.cls);
sink.writeClass(cls);
} else if (typeDeclaration is IndexedMember) {
IndexedMember member = typeDeclaration;
sink.writeEnum(JTypeVariableKind.member);
sink.writeMember(member);
} else if (typeDeclaration is IndexedTypedef) {
IndexedTypedef typedef = typeDeclaration;
sink.writeEnum(JTypeVariableKind.typedef);
sink.writeTypedef(typedef);
} else if (typeDeclaration == null) {
sink.writeEnum(JTypeVariableKind.local);
} else {
throw new UnsupportedError(
"Unexpected type variable declarer $typeDeclaration.");
}
sink.writeString(name);
sink.writeInt(index);
sink.end(tag);
}
String toString() =>
'${jsElementPrefix}type_variable(${typeDeclaration.name}.$name)';
}