blob: ae615b31c38110058f46a58b353f546204ab8c58 [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,
MapEntry,
MapLiteral,
MethodInvocation,
ProcedureKind,
ReturnStatement,
StaticGet,
StringLiteral,
SuperInitializer,
ThisExpression,
VariableGet;
import '../fasta_codes.dart'
show
messageEnumDeclartionEmpty,
messageNoUnnamedConstructorInObject,
templateDuplicatedName,
templateEnumConstantSameNameAsEnclosing;
import '../modifier.dart' show constMask, finalMask, staticMask;
import '../names.dart' show indexGetName;
import '../source/source_class_builder.dart' show SourceClassBuilder;
import 'kernel_builder.dart'
show
Builder,
EnumBuilder,
FormalParameterBuilder,
KernelClassBuilder,
KernelConstructorBuilder,
KernelFieldBuilder,
KernelFormalParameterBuilder,
KernelLibraryBuilder,
KernelNamedTypeBuilder,
KernelProcedureBuilder,
KernelTypeBuilder,
LibraryBuilder,
MemberBuilder,
MetadataBuilder,
Scope;
class KernelEnumBuilder extends SourceClassBuilder
implements EnumBuilder<KernelTypeBuilder, InterfaceType> {
final List<Object> constantNamesAndOffsetsAndDocs;
final MapLiteral toStringMap;
final KernelNamedTypeBuilder intType;
final KernelNamedTypeBuilder stringType;
final KernelNamedTypeBuilder objectType;
final KernelNamedTypeBuilder listType;
KernelEnumBuilder.internal(
String documentationComment,
List<MetadataBuilder> metadata,
String name,
Scope scope,
Scope constructors,
ShadowClass cls,
this.constantNamesAndOffsetsAndDocs,
this.toStringMap,
this.intType,
this.listType,
this.objectType,
this.stringType,
LibraryBuilder parent,
int charOffset)
: super(documentationComment, metadata, 0, name, null, null, null, scope,
constructors, parent, null, charOffset, cls);
factory KernelEnumBuilder(
String documentationComment,
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, charOffset, parent.fileUri);
KernelTypeBuilder stringType =
new KernelNamedTypeBuilder("String", null, charOffset, parent.fileUri);
KernelNamedTypeBuilder objectType =
new KernelNamedTypeBuilder("Object", null, charOffset, parent.fileUri);
ShadowClass cls = new ShadowClass(name: name);
Map<String, MemberBuilder> members = <String, MemberBuilder>{};
Map<String, MemberBuilder> constructors = <String, MemberBuilder>{};
KernelNamedTypeBuilder selfType =
new KernelNamedTypeBuilder(name, null, charOffset, parent.fileUri);
KernelTypeBuilder listType = new KernelNamedTypeBuilder(
"List", <KernelTypeBuilder>[selfType], charOffset, parent.fileUri);
/// From Dart Programming Language Specification 4th Edition/December 2015:
/// metadata class E {
/// final int index;
/// const E(this.index);
/// static const E id0 = const E(0);
/// ...
/// static const E idn-1 = const E(n - 1);
/// static const List<E> values = const <E>[id0, ..., idn-1];
/// String toString() => { 0: ‘E.id0’, . . ., n-1: ‘E.idn-1’}[index]
/// }
members["index"] = new KernelFieldBuilder(null, null, intType, "index",
finalMask, parent, charOffset, null, true);
KernelConstructorBuilder constructorBuilder = new KernelConstructorBuilder(
null,
null,
constMask,
null,
"",
null,
<FormalParameterBuilder>[
new KernelFormalParameterBuilder(
null, 0, intType, "index", true, parent, charOffset)
],
parent,
charOffset,
charOffset,
charEndOffset);
constructors[""] = constructorBuilder;
int index = 0;
List<MapEntry> toStringEntries = <MapEntry>[];
KernelFieldBuilder valuesBuilder = new KernelFieldBuilder(
null,
null,
listType,
"values",
constMask | staticMask,
parent,
charOffset,
null,
true);
members["values"] = valuesBuilder;
KernelProcedureBuilder toStringBuilder = new KernelProcedureBuilder(
null,
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 += 3) {
String name = constantNamesAndOffsetsAndDocs[i];
int charOffset = constantNamesAndOffsetsAndDocs[i + 1];
String documentationComment = constantNamesAndOffsetsAndDocs[i + 2];
if (members.containsKey(name)) {
parent.addCompileTimeError(templateDuplicatedName.withArguments(name),
charOffset, parent.fileUri);
constantNamesAndOffsetsAndDocs[i] = null;
continue;
}
if (name == className) {
parent.addCompileTimeError(
templateEnumConstantSameNameAsEnclosing.withArguments(name),
charOffset,
parent.fileUri);
constantNamesAndOffsetsAndDocs[i] = null;
continue;
}
KernelFieldBuilder fieldBuilder = new KernelFieldBuilder(
documentationComment,
null,
selfType,
name,
constMask | staticMask,
parent,
charOffset,
null,
true);
members[name] = fieldBuilder;
toStringEntries.add(new MapEntry(
new IntLiteral(index), new StringLiteral("$className.$name")));
index++;
}
MapLiteral toStringMap = new MapLiteral(toStringEntries, isConst: true);
KernelEnumBuilder enumBuilder = new KernelEnumBuilder.internal(
documentationComment,
metadata,
name,
new Scope(members, null, parent.scope, "enum $name",
isModifiable: false),
new Scope(constructors, null, null, "constructors",
isModifiable: false),
cls,
constantNamesAndOffsetsAndDocs,
toStringMap,
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);
cls.builder = 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;
if (constantNamesAndOffsetsAndDocs.isEmpty) {
libraryBuilder.addCompileTimeError(
messageEnumDeclartionEmpty, charOffset, fileUri);
}
intType.resolveIn(coreLibrary.scope);
stringType.resolveIn(coreLibrary.scope);
objectType.resolveIn(coreLibrary.scope);
listType.resolveIn(coreLibrary.scope);
toStringMap.keyType = intType.build(libraryBuilder);
toStringMap.valueType = stringType.build(libraryBuilder);
KernelFieldBuilder indexFieldBuilder = this["index"];
Field indexField = indexFieldBuilder.build(libraryBuilder);
KernelProcedureBuilder toStringBuilder = this["toString"];
toStringBuilder.body = new ReturnStatement(new MethodInvocation(
toStringMap,
indexGetName,
new Arguments(<Expression>[
new DirectPropertyGet(new ThisExpression(), indexField)
])));
List<Expression> values = <Expression>[];
for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 3) {
String name = constantNamesAndOffsetsAndDocs[i];
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.single))
..parent = constructor);
KernelClassBuilder objectClass = objectType.builder;
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);
} else {
constructor.initializers.add(
new SuperInitializer(superConstructor.target, new Arguments.empty())
..parent = constructor);
}
int index = 0;
for (int i = 0; i < constantNamesAndOffsetsAndDocs.length; i += 3) {
String constant = constantNamesAndOffsetsAndDocs[i];
if (constant != null) {
KernelFieldBuilder field = this[constant];
field.build(libraryBuilder);
Arguments arguments =
new Arguments(<Expression>[new IntLiteral(index++)]);
field.initializer =
new ConstructorInvocation(constructor, arguments, isConst: true);
}
}
return super.build(libraryBuilder, coreLibrary);
}
@override
Builder findConstructorOrFactory(
String name, int charOffset, Uri uri, LibraryBuilder library) {
return null;
}
}