| // Copyright (c) 2014, 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.resolution.enum_creator; |
| |
| import '../common.dart'; |
| import '../core_types.dart' show CommonElements; |
| import '../dart_types.dart'; |
| import '../elements/elements.dart'; |
| import '../elements/modelx.dart'; |
| import '../tokens/keyword.dart' show Keyword; |
| import '../tokens/precedence.dart'; |
| import '../tokens/precedence_constants.dart' as Precedence; |
| import '../tokens/token.dart'; |
| import '../tree/tree.dart'; |
| import '../util/util.dart'; |
| |
| // TODO(johnniwinther): Merge functionality with the `TreePrinter`. |
| class AstBuilder { |
| final int charOffset; |
| |
| AstBuilder(this.charOffset); |
| |
| Modifiers modifiers( |
| {bool isConst: false, bool isFinal: false, bool isStatic: false}) { |
| List identifiers = []; |
| int flags = 0; |
| if (isConst) { |
| identifiers.add(identifier('const')); |
| flags |= Modifiers.FLAG_CONST; |
| } |
| if (isFinal) { |
| identifiers.add(identifier('final')); |
| flags |= Modifiers.FLAG_FINAL; |
| } |
| if (isStatic) { |
| identifiers.add(identifier('static')); |
| flags |= Modifiers.FLAG_STATIC; |
| } |
| return new Modifiers.withFlags( |
| new NodeList(null, linkedList(identifiers), null, ''), flags); |
| } |
| |
| Token keywordToken(String text) { |
| return new KeywordToken(Keyword.keywords[text], charOffset); |
| } |
| |
| Token stringToken(String text) { |
| return new StringToken.fromString( |
| Precedence.IDENTIFIER_INFO, text, charOffset); |
| } |
| |
| Token symbolToken(PrecedenceInfo info) { |
| return new SymbolToken(info, charOffset); |
| } |
| |
| Identifier identifier(String text) { |
| Keyword keyword = Keyword.keywords[text]; |
| Token token; |
| if (keyword != null) { |
| token = new KeywordToken(Keyword.keywords[text], charOffset); |
| } else { |
| token = stringToken(text); |
| } |
| return new Identifier(token); |
| } |
| |
| Link linkedList(List elements) { |
| LinkBuilder builder = new LinkBuilder(); |
| elements.forEach((e) => builder.addLast(e)); |
| return builder.toLink(); |
| } |
| |
| NodeList argumentList(List<Node> nodes) { |
| return new NodeList(symbolToken(Precedence.OPEN_PAREN_INFO), |
| linkedList(nodes), symbolToken(Precedence.CLOSE_PAREN_INFO), ','); |
| } |
| |
| Return returnStatement(Expression expression) { |
| return new Return(keywordToken('return'), |
| symbolToken(Precedence.SEMICOLON_INFO), expression); |
| } |
| |
| FunctionExpression functionExpression(Modifiers modifiers, String name, |
| NodeList typeVariables, NodeList argumentList, Statement body, |
| [TypeAnnotation returnType]) { |
| return new FunctionExpression( |
| identifier(name), |
| typeVariables, |
| argumentList, |
| body, |
| returnType, |
| modifiers, |
| null, // Initializer. |
| null, // get/set. |
| null // Async modifier. |
| ); |
| } |
| |
| EmptyStatement emptyStatement() { |
| return new EmptyStatement(symbolToken(Precedence.COMMA_INFO)); |
| } |
| |
| LiteralInt literalInt(int value) { |
| return new LiteralInt(stringToken('$value'), null); |
| } |
| |
| LiteralString literalString(String text, |
| {String prefix: '"', String suffix: '"'}) { |
| return new LiteralString( |
| stringToken('$prefix$text$suffix'), new DartString.literal(text)); |
| } |
| |
| LiteralList listLiteral(List<Node> elements, {bool isConst: false}) { |
| return new LiteralList( |
| null, |
| new NodeList( |
| symbolToken(Precedence.OPEN_SQUARE_BRACKET_INFO), |
| linkedList(elements), |
| symbolToken(Precedence.CLOSE_SQUARE_BRACKET_INFO), |
| ','), |
| isConst ? keywordToken('const') : null); |
| } |
| |
| Node createDefinition(Identifier name, Expression initializer) { |
| if (initializer == null) return name; |
| return new SendSet( |
| null, |
| name, |
| new Operator(symbolToken(Precedence.EQ_INFO)), |
| new NodeList.singleton(initializer)); |
| } |
| |
| VariableDefinitions initializingFormal(String fieldName) { |
| return new VariableDefinitions.forParameter( |
| new NodeList.empty(), |
| null, |
| Modifiers.EMPTY, |
| new NodeList.singleton( |
| new Send(identifier('this'), identifier(fieldName)))); |
| } |
| |
| NewExpression newExpression(String typeName, NodeList arguments, |
| {bool isConst: false}) { |
| return new NewExpression(keywordToken(isConst ? 'const' : 'new'), |
| new Send(null, identifier(typeName), arguments)); |
| } |
| |
| Send reference(Identifier identifier) { |
| return new Send(null, identifier); |
| } |
| |
| Send indexGet(Expression receiver, Expression index) { |
| return new Send(receiver, new Operator(symbolToken(Precedence.INDEX_INFO)), |
| new NodeList.singleton(index)); |
| } |
| |
| LiteralMapEntry mapLiteralEntry(Expression key, Expression value) { |
| return new LiteralMapEntry(key, symbolToken(Precedence.COLON_INFO), value); |
| } |
| |
| LiteralMap mapLiteral(List<LiteralMapEntry> entries, {bool isConst: false}) { |
| return new LiteralMap( |
| null, // Type arguments. |
| new NodeList( |
| symbolToken(Precedence.OPEN_CURLY_BRACKET_INFO), |
| linkedList(entries), |
| symbolToken(Precedence.CLOSE_CURLY_BRACKET_INFO), |
| ','), |
| isConst ? keywordToken('const') : null); |
| } |
| } |
| |
| /// This class generates the model for an enum class. |
| /// |
| /// For instance |
| /// |
| /// enum A { b, c, } |
| /// |
| /// is modelled as |
| /// |
| /// class A { |
| /// final int index; |
| /// |
| /// const A(this.index); |
| /// |
| /// String toString() { |
| /// return const <int, A>{0: 'A.b', 1: 'A.c'}[index]; |
| /// } |
| /// |
| /// static const A b = const A(0); |
| /// static const A c = const A(1); |
| /// |
| /// static const List<A> values = const <A>[b, c]; |
| /// } |
| /// |
| // TODO(johnniwinther): Avoid creating synthesized ASTs for enums when SSA is |
| // removed. |
| class EnumCreator { |
| final DiagnosticReporter reporter; |
| final CommonElements commonElements; |
| final EnumClassElementX enumClass; |
| |
| EnumCreator(this.reporter, this.commonElements, this.enumClass); |
| |
| void createMembers() { |
| Enum node = enumClass.node; |
| InterfaceType enumType = enumClass.thisType; |
| AstBuilder builder = new AstBuilder(enumClass.position.charOffset); |
| |
| InterfaceType intType = commonElements.intType; |
| InterfaceType stringType = commonElements.stringType; |
| |
| EnumFieldElementX addInstanceMember(String name, InterfaceType type) { |
| Identifier identifier = builder.identifier(name); |
| VariableList variableList = |
| new VariableList(builder.modifiers(isFinal: true)); |
| variableList.type = type; |
| EnumFieldElementX variable = new EnumFieldElementX( |
| identifier, enumClass, variableList, identifier); |
| enumClass.addMember(variable, reporter); |
| return variable; |
| } |
| |
| EnumFieldElementX indexVariable = addInstanceMember('index', intType); |
| |
| VariableDefinitions indexDefinition = builder.initializingFormal('index'); |
| |
| FunctionExpression constructorNode = builder.functionExpression( |
| builder.modifiers(isConst: true), |
| enumClass.name, |
| null, // typeVariables |
| builder.argumentList([indexDefinition]), |
| builder.emptyStatement()); |
| |
| EnumConstructorElementX constructor = new EnumConstructorElementX( |
| enumClass, builder.modifiers(isConst: true), constructorNode); |
| |
| EnumFormalElementX indexFormal = new EnumFormalElementX(constructor, |
| indexDefinition, builder.identifier('index'), indexVariable); |
| |
| FunctionSignatureX constructorSignature = new FunctionSignatureX( |
| requiredParameters: [indexFormal], |
| requiredParameterCount: 1, |
| type: new FunctionType( |
| constructor, const DynamicType(), <DartType>[intType])); |
| constructor.functionSignature = constructorSignature; |
| enumClass.addMember(constructor, reporter); |
| |
| List<EnumConstantElement> enumValues = <EnumConstantElement>[]; |
| int index = 0; |
| List<Node> valueReferences = <Node>[]; |
| List<LiteralMapEntry> mapEntries = <LiteralMapEntry>[]; |
| for (Link<Node> link = node.names.nodes; !link.isEmpty; link = link.tail) { |
| Identifier name = link.head; |
| AstBuilder valueBuilder = new AstBuilder(name.token.charOffset); |
| VariableList variableList = new VariableList( |
| valueBuilder.modifiers(isStatic: true, isConst: true)); |
| variableList.type = enumType; |
| |
| // Add reference for the `values` field. |
| valueReferences.add(valueBuilder.reference(name)); |
| |
| // Add map entry for `toString` implementation. |
| mapEntries.add(valueBuilder.mapLiteralEntry( |
| valueBuilder.literalInt(index), |
| valueBuilder.literalString('${enumClass.name}.${name.source}'))); |
| |
| Expression initializer = valueBuilder.newExpression(enumClass.name, |
| valueBuilder.argumentList([valueBuilder.literalInt(index)]), |
| isConst: true); |
| SendSet definition = valueBuilder.createDefinition(name, initializer); |
| |
| EnumConstantElementX field = new EnumConstantElementX( |
| name, enumClass, variableList, definition, initializer, index); |
| enumValues.add(field); |
| enumClass.addMember(field, reporter); |
| index++; |
| } |
| |
| VariableList valuesVariableList = |
| new VariableList(builder.modifiers(isStatic: true, isConst: true)); |
| valuesVariableList.type = commonElements.listType(enumType); |
| |
| Identifier valuesIdentifier = builder.identifier('values'); |
| // TODO(johnniwinther): Add type argument. |
| Expression initializer = |
| builder.listLiteral(valueReferences, isConst: true); |
| |
| Node definition = builder.createDefinition(valuesIdentifier, initializer); |
| |
| EnumFieldElementX valuesVariable = new EnumFieldElementX(valuesIdentifier, |
| enumClass, valuesVariableList, definition, initializer); |
| |
| enumClass.addMember(valuesVariable, reporter); |
| |
| // TODO(johnniwinther): Support return type. Note `String` might be prefixed |
| // or not imported within the current library. |
| FunctionExpression toStringNode = builder.functionExpression( |
| Modifiers.EMPTY, |
| 'toString', |
| null, // typeVariables |
| builder.argumentList([]), |
| builder.returnStatement(builder.indexGet( |
| builder.mapLiteral(mapEntries, isConst: true), |
| builder.reference(builder.identifier('index'))))); |
| |
| EnumMethodElementX toString = new EnumMethodElementX( |
| 'toString', enumClass, Modifiers.EMPTY, toStringNode); |
| FunctionSignatureX toStringSignature = |
| new FunctionSignatureX(type: new FunctionType(toString, stringType)); |
| toString.functionSignature = toStringSignature; |
| enumClass.addMember(toString, reporter); |
| |
| enumClass.enumValues = enumValues; |
| } |
| } |