blob: 5c4c3d96e5d54961a52ddbc2a77d246700b653b1 [file] [log] [blame]
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library fasta.kernel_enum_builder;
import 'package:front_end/src/fasta/kernel/kernel_shadow_ast.dart'
show ShadowClass;
import 'package:kernel/ast.dart'
show
Arguments,
Class,
Constructor,
ConstructorInvocation,
DirectPropertyGet,
Expression,
Field,
FieldInitializer,
IntLiteral,
InterfaceType,
ListLiteral,
ProcedureKind,
ReturnStatement,
StaticGet,
StringLiteral,
SuperInitializer,
ThisExpression,
TreeNode,
VariableGet;
import '../fasta_codes.dart'
show
messageNoUnnamedConstructorInObject,
noLength,
templateDuplicatedName,
templateEnumConstantSameNameAsEnclosing;
import '../modifier.dart' show constMask, finalMask, staticMask;
import '../source/source_class_builder.dart' show SourceClassBuilder;
import 'kernel_builder.dart'
show
Declaration,
EnumBuilder,
FormalParameterBuilder,
KernelClassBuilder,
KernelConstructorBuilder,
KernelFieldBuilder,
KernelFormalParameterBuilder,
KernelLibraryBuilder,
KernelNamedTypeBuilder,
KernelProcedureBuilder,
KernelTypeBuilder,
LibraryBuilder,
MemberBuilder,
MetadataBuilder,
Scope;
import 'metadata_collector.dart';
class KernelEnumBuilder extends SourceClassBuilder
implements EnumBuilder<KernelTypeBuilder, InterfaceType> {
final List<Object> constantNamesAndOffsetsAndDocs;
final KernelNamedTypeBuilder intType;
final KernelNamedTypeBuilder stringType;
final KernelNamedTypeBuilder objectType;
final KernelNamedTypeBuilder listType;
KernelEnumBuilder.internal(
List<MetadataBuilder> metadata,
String name,
Scope scope,
Scope constructors,
ShadowClass cls,
this.constantNamesAndOffsetsAndDocs,
this.intType,
this.listType,
this.objectType,
this.stringType,
LibraryBuilder parent,
int charOffset)
: super(metadata, 0, name, null, null, null, scope, constructors, parent,
null, charOffset, TreeNode.noOffset, cls);
factory KernelEnumBuilder(
MetadataCollector metadataCollector,
List<MetadataBuilder> metadata,
String name,
List<Object> constantNamesAndOffsetsAndDocs,
KernelLibraryBuilder parent,
int charOffset,
int charEndOffset) {
constantNamesAndOffsetsAndDocs ??= const <Object>[];
// TODO(ahe): These types shouldn't be looked up in scope, they come
// directly from dart:core.
KernelTypeBuilder intType = new KernelNamedTypeBuilder("int", null);
KernelTypeBuilder stringType = new KernelNamedTypeBuilder("String", null);
KernelNamedTypeBuilder objectType =
new KernelNamedTypeBuilder("Object", null);
ShadowClass cls = new ShadowClass(name: name);
Map<String, MemberBuilder> members = <String, MemberBuilder>{};
Map<String, MemberBuilder> constructors = <String, MemberBuilder>{};
KernelNamedTypeBuilder selfType = new KernelNamedTypeBuilder(name, null);
KernelTypeBuilder listType =
new KernelNamedTypeBuilder("List", <KernelTypeBuilder>[selfType]);
/// metadata class E {
/// final int index;
/// final String _name;
/// const E(this.index, this._name);
/// static const E id0 = const E(0, 'E.id0');
/// ...
/// static const E idn-1 = const E(n - 1, 'E.idn-1');
/// static const List<E> values = const <E>[id0, ..., idn-1];
/// String toString() => _name;
/// }
members["index"] = new KernelFieldBuilder(
null, intType, "index", finalMask, parent, charOffset, null, true);
members["_name"] = new KernelFieldBuilder(
null, stringType, "_name", finalMask, parent, charOffset, null, true);
KernelConstructorBuilder constructorBuilder = new KernelConstructorBuilder(
null,
constMask,
null,
"",
null,
<FormalParameterBuilder>[
new KernelFormalParameterBuilder(
null, 0, intType, "index", true, parent, charOffset),
new KernelFormalParameterBuilder(
null, 0, stringType, "_name", true, parent, charOffset)
],
parent,
charOffset,
charOffset,
charEndOffset);
constructors[""] = constructorBuilder;
KernelFieldBuilder valuesBuilder = new KernelFieldBuilder(null, listType,
"values", constMask | staticMask, parent, charOffset, null, true);
members["values"] = valuesBuilder;
KernelProcedureBuilder toStringBuilder = new KernelProcedureBuilder(
null,
0,
stringType,
"toString",
null,
null,
ProcedureKind.Method,
parent,
charOffset,
charOffset,
charEndOffset);
members["toString"] = toStringBuilder;
String className = name;
for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 4) {
List<MetadataBuilder> metadata = constantNamesAndOffsetsAndDocs[i];
String name = constantNamesAndOffsetsAndDocs[i + 1];
int charOffset = constantNamesAndOffsetsAndDocs[i + 2];
String documentationComment = constantNamesAndOffsetsAndDocs[i + 3];
if (members.containsKey(name)) {
parent.addCompileTimeError(templateDuplicatedName.withArguments(name),
charOffset, noLength, parent.fileUri);
constantNamesAndOffsetsAndDocs[i + 1] = null;
continue;
}
if (name == className) {
parent.addCompileTimeError(
templateEnumConstantSameNameAsEnclosing.withArguments(name),
charOffset,
noLength,
parent.fileUri);
constantNamesAndOffsetsAndDocs[i + 1] = null;
continue;
}
KernelFieldBuilder fieldBuilder = new KernelFieldBuilder(
metadata,
selfType,
name,
constMask | staticMask,
parent,
charOffset,
null,
true);
metadataCollector?.setDocumentationComment(
fieldBuilder.target, documentationComment);
members[name] = fieldBuilder;
}
KernelEnumBuilder enumBuilder = new KernelEnumBuilder.internal(
metadata,
name,
new Scope(members, null, parent.scope, "enum $name",
isModifiable: false),
new Scope(constructors, null, null, "constructors",
isModifiable: false),
cls,
constantNamesAndOffsetsAndDocs,
intType,
listType,
objectType,
stringType,
parent,
charOffset);
// TODO(sigmund): dynamic should be `covariant MemberBuilder`.
void setParent(String name, dynamic b) {
MemberBuilder builder = b;
builder.parent = enumBuilder;
}
members.forEach(setParent);
constructors.forEach(setParent);
selfType.bind(enumBuilder);
ShadowClass.setBuilder(cls, enumBuilder);
return enumBuilder;
}
KernelTypeBuilder get mixedInType => null;
InterfaceType buildType(
LibraryBuilder library, List<KernelTypeBuilder> arguments) {
return cls.rawType;
}
@override
Class build(KernelLibraryBuilder libraryBuilder, LibraryBuilder coreLibrary) {
cls.isEnum = true;
intType.resolveIn(coreLibrary.scope, charOffset, fileUri);
stringType.resolveIn(coreLibrary.scope, charOffset, fileUri);
objectType.resolveIn(coreLibrary.scope, charOffset, fileUri);
listType.resolveIn(coreLibrary.scope, charOffset, fileUri);
KernelFieldBuilder indexFieldBuilder = this["index"];
Field indexField = indexFieldBuilder.build(libraryBuilder);
KernelFieldBuilder nameFieldBuilder = this["_name"];
Field nameField = nameFieldBuilder.build(libraryBuilder);
KernelProcedureBuilder toStringBuilder = this["toString"];
toStringBuilder.body = new ReturnStatement(
new DirectPropertyGet(new ThisExpression(), nameField));
List<Expression> values = <Expression>[];
for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 4) {
String name = constantNamesAndOffsetsAndDocs[i + 1];
if (name != null) {
KernelFieldBuilder builder = this[name];
values.add(new StaticGet(builder.build(libraryBuilder)));
}
}
KernelFieldBuilder valuesBuilder = this["values"];
valuesBuilder.build(libraryBuilder);
valuesBuilder.initializer =
new ListLiteral(values, typeArgument: cls.rawType, isConst: true);
KernelConstructorBuilder constructorBuilder = constructorScopeBuilder[""];
Constructor constructor = constructorBuilder.build(libraryBuilder);
constructor.initializers.insert(
0,
new FieldInitializer(indexField,
new VariableGet(constructor.function.positionalParameters[0]))
..parent = constructor);
constructor.initializers.insert(
1,
new FieldInitializer(nameField,
new VariableGet(constructor.function.positionalParameters[1]))
..parent = constructor);
KernelClassBuilder objectClass = objectType.declaration;
MemberBuilder superConstructor = objectClass.findConstructorOrFactory(
"", charOffset, fileUri, libraryBuilder);
if (superConstructor == null || !superConstructor.isConstructor) {
// TODO(ahe): Ideally, we would also want to check that [Object]'s
// unnamed constructor requires no arguments. But that information isn't
// always available at this point, and it's not really a situation that
// can happen unless you start modifying the SDK sources.
addCompileTimeError(messageNoUnnamedConstructorInObject, -1, noLength);
} else {
constructor.initializers.add(
new SuperInitializer(superConstructor.target, new Arguments.empty())
..parent = constructor);
}
int index = 0;
for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 4) {
String constant = constantNamesAndOffsetsAndDocs[i + 1];
if (constant != null) {
KernelFieldBuilder field = this[constant];
field.build(libraryBuilder);
Arguments arguments = new Arguments(<Expression>[
new IntLiteral(index++),
new StringLiteral("$name.$constant")
]);
field.initializer =
new ConstructorInvocation(constructor, arguments, isConst: true);
}
}
return super.build(libraryBuilder, coreLibrary);
}
@override
Declaration findConstructorOrFactory(
String name, int charOffset, Uri uri, LibraryBuilder library) {
return null;
}
}