| // 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. |
| |
| /// Logic to build unlinked summaries. |
| library summary.src.summary_builder; |
| |
| import 'package:front_end/src/fasta/parser/class_member_parser.dart'; |
| import 'package:front_end/src/fasta/parser/identifier_context.dart'; |
| import 'package:front_end/src/fasta/parser/parser.dart'; |
| import 'package:front_end/src/fasta/scanner.dart'; |
| import 'package:front_end/src/fasta/scanner/token_constants.dart'; |
| |
| import 'expression_serializer.dart'; |
| import 'model.dart'; |
| import 'stack_listener.dart'; |
| |
| const _abstract_flag = 1 << 2; |
| |
| const _async_flag = 1; |
| |
| const _const_flag = 1 << 1; |
| |
| const _external_flag = 1 << 4; |
| |
| const _final_flag = 1; |
| |
| /// Maps modifier names to their bit-mask. |
| const _modifierFlag = const { |
| 'const': _const_flag, |
| 'abstract': _abstract_flag, |
| 'static': _static_flag, |
| 'external': _external_flag, |
| 'final': _final_flag, |
| 'var': _var_flag, |
| }; |
| |
| // bit-masks to encode modifiers as bits on an int. |
| |
| const _star_flag = 1 << 2; |
| |
| const _static_flag = 1 << 3; |
| const _sync_flag = 1 << 1; |
| const _var_flag = 0; |
| |
| /// Retrieve the operator from an assignment operator (e.g. + from +=). |
| /// Operators are encoded using the scanner token kind id. |
| int opForAssignOp(int kind) { |
| switch (kind) { |
| case AMPERSAND_EQ_TOKEN: |
| return AMPERSAND_TOKEN; |
| // TODO(paulberry): add support for &&= |
| // case AMPERSAND_AMPERSAND_EQ_TOKEN: return AMPERSAND_AMPERSAND_TOKEN; |
| case BAR_EQ_TOKEN: |
| return BAR_TOKEN; |
| // TODO(paulberry): add support for ||= |
| // case BAR_BAR_EQ_TOKEN: return BAR_BAR_TOKEN; |
| case CARET_EQ_TOKEN: |
| return CARET_TOKEN; |
| case GT_GT_EQ_TOKEN: |
| return GT_GT_TOKEN; |
| case LT_LT_EQ_TOKEN: |
| return LT_LT_TOKEN; |
| case MINUS_EQ_TOKEN: |
| return MINUS_TOKEN; |
| case PERCENT_EQ_TOKEN: |
| return PERCENT_TOKEN; |
| case PLUS_EQ_TOKEN: |
| return PLUS_TOKEN; |
| case QUESTION_QUESTION_EQ_TOKEN: |
| return QUESTION_QUESTION_TOKEN; |
| case SLASH_EQ_TOKEN: |
| return SLASH_TOKEN; |
| case STAR_EQ_TOKEN: |
| return STAR_TOKEN; |
| case TILDE_SLASH_EQ_TOKEN: |
| return TILDE_SLASH_TOKEN; |
| case PLUS_EQ_TOKEN: |
| return PLUS_TOKEN; |
| default: |
| throw "Unhandled kind $kind"; |
| } |
| } |
| |
| /// Create an unlinked summary given a null-terminated byte buffer with the |
| /// contents of a file. |
| UnlinkedUnit summarize(Uri uri, List<int> contents) { |
| var listener = new SummaryBuilder(uri); |
| var parser = new ClassMemberParser(listener); |
| parser.parseUnit(scan(contents).tokens); |
| return listener.topScope.unit; |
| } |
| |
| /// Builder for constant expressions. |
| /// |
| /// Any invalid subexpression is denoted with [Invalid]. |
| class ConstExpressionBuilder extends ExpressionListener { |
| final Uri uri; |
| Parser parser; |
| ConstExpressionBuilder(this.uri) { |
| parser = new Parser(this); |
| } |
| bool get forConst => true; |
| |
| void endArguments(int count, Token begin, Token end) { |
| debugEvent("Arguments"); |
| if (ignore) return; |
| push(popList(count) ?? const []); |
| } |
| |
| void handleAsOperator(Token op, Token next) { |
| debugEvent("As"); |
| if (ignore) return; |
| push(new As(pop(), pop())); |
| } |
| |
| void handleAssignmentExpression(Token operator) { |
| pop(); // lhs |
| pop(); // rhs |
| push(new Invalid(hint: "assign")); |
| } |
| |
| void handleIndexedExpression(Token openSquareBracket, Token token) { |
| debugEvent("Index"); |
| if (ignore) return; |
| pop(); // receiver |
| pop(); // index |
| push(new Invalid(hint: "index")); |
| } |
| |
| void handleNamedArgument(colon) { |
| debugEvent("NamedArg"); |
| if (ignore) return; |
| var value = pop(); |
| Ref name = pop(); |
| push(new NamedArg(name.name, value)); |
| } |
| |
| void endNewExpression(Token token) { |
| debugEvent("NewExpression"); |
| if (ignore) return; |
| pop(); // args |
| pop(); // ctor |
| push(new Invalid(hint: "new")); |
| } |
| |
| void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { |
| debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); |
| } |
| |
| void handleUnaryPostfixAssignmentExpression(Token operator) { |
| pop(); |
| push(new Invalid(hint: "postfixOp")); |
| } |
| |
| void handleUnaryPrefixAssignmentExpression(Token operator) { |
| pop(); |
| push(new Invalid(hint: "prefixOp")); |
| } |
| |
| void _endFunction() { |
| assert(_withinFunction >= 0); |
| push(new Invalid(hint: 'function')); |
| } |
| |
| // TODO(paulberry): is this needed? |
| //void _endCascade() { |
| // push(new Invalid(hint: 'cascades')); |
| //} |
| |
| void _unhandledSend() { |
| push(new Invalid(hint: "call")); |
| } |
| } |
| |
| // bit-masks to encode async modifiers as bits on an int. |
| |
| /// Parser listener to build simplified AST expresions. |
| /// |
| /// The parser produces different trees depending on whether it is used for |
| /// constants or initializers, so subclasses specialize the logic accordingly. |
| abstract class ExpressionListener extends StackListener { |
| // Underlying parser that invokes this listener. |
| static const _invariantCheckToken = "invariant check: starting a function"; |
| |
| int _withinFunction = 0; |
| |
| int _withinCascades = 0; |
| |
| /// Whether this listener is used to build const expressions. |
| bool get forConst => false; |
| |
| /// Whether to ignore the next reduction. Used to ignore nested expresions |
| /// that are either invalid (in constants) or unnecessary (for initializers). |
| bool get ignore => _withinFunction > 0 || _withinCascades > 0; |
| |
| Parser get parser; |
| |
| void beginCascade(Token token) { |
| _withinCascades++; |
| } |
| |
| void beginFunctionDeclaration(token) { |
| debugEvent("BeginFunctionDeclaration"); |
| _withinFunction++; |
| } |
| |
| void beginLiteralString(Token token) { |
| debugEvent("beginLiteralString"); |
| if (ignore) return; |
| push(new StringLiteral(token.lexeme)); |
| } |
| |
| void beginUnnamedFunction(token) { |
| debugEvent("BeginUnnamedFunction"); |
| var check = pop(); |
| assert(check == _invariantCheckToken); |
| _withinFunction++; |
| } |
| |
| UnlinkedExprBuilder computeExpression(Token token, Scope scope) { |
| debugStart(token); |
| parser.parseExpression(token); |
| debugEvent('---- END ---'); |
| Expression node = pop(); |
| checkEmpty(); |
| return new Serializer(scope, forConst).run(node); |
| } |
| |
| void debugEvent(String name) { |
| if (const bool.fromEnvironment('CDEBUG', defaultValue: false)) { |
| var s = stack.join(' :: '); |
| if (s == '') s = '<empty>'; |
| var bits = '$_withinFunction,$_withinCascades'; |
| var prefix = ignore ? "ignore $name on:" : "do $name on:"; |
| prefix = '$prefix${" " * (30 - prefix.length)}'; |
| print('$prefix $bits $s'); |
| } |
| } |
| |
| void debugStart(Token token) { |
| debugEvent('\n---- START: $runtimeType ---'); |
| if (const bool.fromEnvironment('CDEBUG', defaultValue: false)) { |
| _printExpression(token); |
| } |
| } |
| |
| void endCascade() { |
| _withinCascades--; |
| throw new UnimplementedError(); // TODO(paulberry): fix the code below. |
| // _endCascade(); |
| } |
| |
| void endConstructorReference( |
| Token start, Token periodBeforeName, Token endToken) { |
| debugEvent("ConstructorReference $start $periodBeforeName"); |
| Ref ctorName = popIfNotNull(periodBeforeName); |
| assert(ctorName?.prefix == null); |
| List<TypeRef> typeArgs = pop(); |
| Ref type = pop(); |
| push(new ConstructorName(new TypeRef(type, typeArgs), ctorName?.name)); |
| } |
| |
| void endFormalParameter(Token covariantKeyword, Token thisKeyword, |
| Token nameToken, FormalParameterType kind) { |
| debugEvent("FormalParameter"); |
| assert(ignore); |
| } |
| |
| void endFormalParameters(int c, begin, end) { |
| debugEvent("FormalParameters"); |
| assert(ignore); |
| } |
| |
| void endBlockFunctionBody(int count, Token begin, Token end) { |
| debugEvent("BlockFunctionBody"); |
| assert(ignore); |
| } |
| |
| void endFunctionDeclaration(token) { |
| debugEvent("FunctionDeclaration"); |
| _withinFunction--; |
| if (ignore) return; |
| _endFunction(); |
| } |
| |
| void endFunctionName(Token beginToken, Token token) { |
| debugEvent("FunctionName"); |
| assert(ignore); |
| } |
| |
| void endLiteralMapEntry(colon, token) { |
| debugEvent('MapEntry'); |
| if (ignore) return; |
| var value = pop(); |
| var key = pop(); |
| push(new KeyValuePair(key, value)); |
| } |
| |
| void endLiteralString(int interpolationCount, Token endToken) { |
| debugEvent("endLiteralString"); |
| if (interpolationCount != 0) { |
| popList(2 * interpolationCount + 1); |
| push(new StringLiteral("<interpolate $interpolationCount>")); |
| } |
| } |
| |
| void endLiteralSymbol(token, int dots) { |
| debugEvent('LiteralSymbol'); |
| if (ignore) return; |
| push(new SymbolLiteral(popList(dots).join('.'))); |
| } |
| |
| void endReturnStatement(hasValue, Token begin, Token end) { |
| debugEvent("ReturnStatement"); |
| assert(ignore); |
| } |
| |
| // type-arguments are expected to be type references passed to constructors |
| // and generic methods, we need them to model instantiations. |
| void endSend(Token beginToken, Token endToken) { |
| debugEvent("EndSend"); |
| if (ignore) return; |
| List<Expression> args = pop(); |
| if (args != null) { |
| /* var typeArgs = */ pop(); |
| var receiver = pop(); |
| // TODO(sigmund): consider making identical a binary operator. |
| if (receiver is Ref && receiver.name == 'identical') { |
| assert(receiver.prefix == null); |
| assert(args.length == 2); |
| push(new Identical(args[0], args[1])); |
| return; |
| } |
| _unhandledSend(); |
| } |
| } |
| |
| void endThrowExpression(throwToken, token) { |
| debugEvent("Throw"); |
| assert(ignore); |
| } |
| |
| void endTypeArguments(int count, Token beginToken, Token endToken) { |
| debugEvent("TypeArguments"); |
| if (ignore) return; |
| push(popList(count) ?? const <TypeRef>[]); |
| } |
| |
| void endTypeList(int count) { |
| debugEvent("TypeList"); |
| push(popList(count) ?? const <TypeRef>[]); |
| } |
| |
| void endTypeVariable(Token token, Token extendsOrSuper) { |
| debugEvent("endTypeVariable"); |
| assert(ignore); |
| } |
| |
| void endTypeVariables(int count, Token beginToken, Token endToken) { |
| debugEvent("TypeVariables"); |
| assert(ignore); |
| } |
| |
| void endUnnamedFunction(Token beginToken, Token token) { |
| debugEvent("UnnamedFunction"); |
| _withinFunction--; |
| if (ignore) return; |
| _endFunction(); |
| } |
| |
| void handleAsyncModifier(Token asyncToken, Token starToken) { |
| debugEvent("AsyncModifier"); |
| assert(ignore); |
| } |
| |
| void handleBinaryExpression(Token operator) { |
| debugEvent("BinaryExpression"); |
| if (ignore) return; |
| Expression right = pop(); |
| Expression left = pop(); |
| var kind = operator.kind; |
| if (kind == PERIOD_TOKEN) { |
| if (left is Ref && |
| right is Ref && |
| right.prefix == null && |
| left.prefixDepth < 2) { |
| push(new Ref(right.name, left)); |
| return; |
| } |
| if (right is Ref) { |
| push(new Load(left, right.name)); |
| return; |
| } |
| } |
| push(new Binary(left, right, kind)); |
| } |
| |
| void handleConditionalExpression(Token question, Token colon) { |
| debugEvent("ConditionalExpression"); |
| if (ignore) return; |
| var falseBranch = pop(); |
| var trueBranch = pop(); |
| var cond = pop(); |
| push(new Conditional(cond, trueBranch, falseBranch)); |
| } |
| |
| @override |
| void endConstExpression(Token token) { |
| debugEvent("ConstExpression"); |
| if (ignore) return; |
| List args = pop(); |
| var constructorName = pop(); |
| var positional = args.where((a) => a is! NamedArg).toList(); |
| var named = args.where((a) => a is NamedArg).toList(); |
| push(new ConstCreation(constructorName, positional, named)); |
| } |
| |
| void handleIdentifier(Token token, IdentifierContext context) { |
| debugEvent("Identifier"); |
| if (ignore) return; |
| push(new Ref(token.lexeme)); |
| } |
| |
| void handleIsOperator(Token operator, Token not, Token endToken) { |
| debugEvent("Is"); |
| if (ignore) return; |
| push(new Is(pop(), pop())); |
| } |
| |
| void handleLiteralBool(Token token) { |
| debugEvent("LiteralBool"); |
| if (ignore) return; |
| push(new BoolLiteral(token.lexeme == 'true')); |
| } |
| |
| void handleLiteralDouble(Token token) { |
| debugEvent("LiteralDouble"); |
| if (ignore) return; |
| push(new DoubleLiteral(double.parse(token.lexeme))); |
| } |
| |
| void handleLiteralInt(Token token) { |
| debugEvent("LiteralInt"); |
| if (ignore) return; |
| push(new IntLiteral(int.parse(token.lexeme))); |
| } |
| |
| void handleLiteralList(count, begin, constKeyword, end) { |
| debugEvent("LiteralList"); |
| if (ignore) return; |
| var values = popList(count) ?? const <Expression>[]; |
| List<TypeRef> typeArguments = pop(); |
| var type = typeArguments?.single; |
| push(new ListLiteral(type, values, constKeyword != null)); |
| } |
| |
| void handleLiteralMap(count, begin, constKeyword, end) { |
| debugEvent('LiteralMap'); |
| if (ignore) return; |
| var values = popList(count) ?? const <KeyValuePair>[]; |
| var typeArgs = pop() ?? const <TypeRef>[]; |
| push(new MapLiteral(typeArgs, values, constKeyword != null)); |
| } |
| |
| void handleLiteralNull(Token token) { |
| debugEvent("LiteralNull"); |
| if (ignore) return; |
| push(new NullLiteral()); |
| } |
| |
| void handleModifier(Token token) { |
| debugEvent("Modifier"); |
| assert(ignore); |
| } |
| |
| // TODO(sigmund): remove |
| void handleModifiers(int count) { |
| debugEvent("Modifiers"); |
| assert(ignore); |
| } |
| |
| // type-variables are the declared parameters on declarations. |
| void handleNoArguments(Token token) { |
| debugEvent("NoArguments"); |
| if (ignore) return; |
| var typeArguments = pop(); |
| assert(typeArguments == null); |
| push(NullValue.Arguments); |
| } |
| |
| void handleNoFormalParameters(Token token) { |
| debugEvent("NoFormalParameters"); |
| assert(ignore); |
| } |
| |
| void handleNoFunctionBody(Token token) { |
| debugEvent("NoFunctionBody"); |
| assert(ignore); |
| } |
| |
| void handleNoInitializer() {} |
| |
| void handleNoInitializers() { |
| debugEvent("NoInitializers"); |
| assert(ignore); |
| } |
| |
| void handleNoType(Token token) { |
| debugEvent("NoType"); |
| if (ignore) return; |
| push(NullValue.Type); |
| } |
| |
| void handleNoTypeArguments(Token token) { |
| debugEvent("NoTypeArguments"); |
| if (ignore) return; |
| push(NullValue.TypeArguments); |
| } |
| |
| void handleNoTypeVariables(Token token) { |
| debugEvent("NoTypeVariables"); |
| if (ignore) return; |
| push(_invariantCheckToken); |
| } |
| |
| void handleQualified(period) { |
| debugEvent('Qualified'); |
| if (ignore) return; |
| Ref name = pop(); |
| Ref prefix = pop(); |
| assert(name.prefix == null); |
| assert(prefix.prefix == null); |
| push(new Ref(name.name, prefix)); |
| } |
| |
| void handleStringJuxtaposition(int count) { |
| debugEvent("StringJuxtaposition"); |
| if (ignore) return; |
| popList(count); |
| push(new StringLiteral('<juxtapose $count>')); |
| } |
| |
| void handleStringPart(token) { |
| debugEvent("handleStringPart"); |
| if (ignore) return; |
| push(new StringLiteral(token.lexeme)); |
| } |
| |
| void handleType(Token beginToken, Token endToken) { |
| debugEvent("Type"); |
| if (ignore) return; |
| List<TypeRef> arguments = pop(); |
| Ref name = pop(); |
| push(new TypeRef(name, arguments)); |
| } |
| |
| void handleUnaryPrefixExpression(Token operator) { |
| debugEvent("UnaryPrefix"); |
| if (ignore) return; |
| push(new Unary(pop(), operator.kind)); |
| } |
| |
| void handleVoidKeyword(Token token) { |
| debugEvent("VoidKeyword"); |
| assert(ignore); |
| } |
| |
| /// Overriden: the base class throws when something is not handled, we avoid |
| /// implementing a few handlers when we know we can ignore them. |
| @override |
| void logEvent(e) { |
| if (ignore) return; |
| super.logEvent(e); |
| } |
| |
| void push(Object o); |
| |
| // debug helpers |
| |
| void _endFunction(); |
| |
| void _printExpression(Token token) { |
| var current = token; |
| var end = new ClassMemberParser(this).skipExpression(current); |
| var str = new StringBuffer(); |
| while (current != end) { |
| if (!["(", ",", ")"].contains(current.lexeme)) str.write(' '); |
| str.write(current.lexeme); |
| current = current.next; |
| } |
| print('exp: $str'); |
| } |
| |
| void _unhandledSend(); |
| } |
| |
| /// Builder for initializer expressions. These expressions exclude any nested |
| /// expression that is not needed to infer strong mode types. |
| class InitializerBuilder extends ExpressionListener { |
| final Uri uri; |
| Parser parser; |
| |
| int _inArguments = 0; |
| |
| InitializerBuilder(this.uri) { |
| parser = new Parser(this); |
| } |
| |
| bool get ignore => super.ignore || _inArguments > 0; |
| |
| void beginArguments(Token token) { |
| // TODO(sigmund): determine if we can ignore arguments. |
| //_inArguments++; |
| } |
| |
| // Not necessary, but we don't use the value, so we can abstract it: |
| void endArguments(int count, Token begin, Token end) { |
| debugEvent("Arguments"); |
| //_inArguments--; |
| if (ignore) return; |
| push(popList(count) ?? const []); |
| //push([new Opaque(hint: "arguments")]); |
| } |
| |
| void handleAsOperator(Token op, Token next) { |
| debugEvent("As"); |
| if (ignore) return; |
| TypeRef type = pop(); |
| pop(); |
| push(new Opaque(type: type)); |
| } |
| |
| void handleAssignmentExpression(Token operator) { |
| debugEvent("Assign"); |
| if (ignore) return; |
| var left = pop(); |
| var right = pop(); |
| var kind = operator.kind; |
| if (kind == EQ_TOKEN) { |
| push(new OpaqueOp(right)); |
| } else { |
| push(new OpaqueOp(new Binary(left, right, opForAssignOp(kind)))); |
| } |
| } |
| |
| void handleIndexedExpression(Token openSquareBracket, Token token) { |
| debugEvent("Index"); |
| if (ignore) return; |
| pop(); |
| pop(); |
| push(new Opaque()); |
| } |
| |
| void handleIsOperator(Token operator, Token not, Token endToken) { |
| debugEvent("Is"); |
| if (ignore) return; |
| throw new UnimplementedError(); // TODO(paulberry): fix the code below. |
| // push(new Opaque(type: new TypeRef(new Ref('bool')))); |
| } |
| |
| void handleNamedArgument(colon) { |
| debugEvent("NamedArg"); |
| if (ignore) return; |
| pop(); |
| pop(); |
| push(NullValue.Arguments); |
| } |
| |
| void endNewExpression(Token token) { |
| debugEvent("NewExpression"); |
| if (ignore) return; |
| pop(); // args |
| /* var ctor = */ pop(); // ctor |
| throw new UnimplementedError(); // TODO(paulberry): fix the code below. |
| // push(new Opaque(type: ctor.type, hint: "new")); |
| } |
| |
| void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { |
| debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); |
| } |
| |
| void handleUnaryPostfixAssignmentExpression(Token operator) { |
| debugEvent("PostFix"); |
| if (ignore) return; |
| // the post-fix effect is not visible to the enclosing expression |
| push(new OpaqueOp(pop())); |
| } |
| |
| void handleUnaryPrefixAssignmentExpression(Token operator) { |
| debugEvent("Prefix"); |
| if (ignore) return; |
| var kind = operator.kind == PLUS_PLUS_TOKEN ? PLUS_TOKEN : MINUS_TOKEN; |
| push(new OpaqueOp(new Binary(pop(), new IntLiteral(1), kind))); |
| } |
| |
| void _endFunction() { |
| push(new Opaque(hint: "function")); |
| } |
| |
| // TODO(paulberry): is this needed? |
| //void _endCascade() { |
| // push(new OpaqueOp(pop(), hint: 'cascades')); |
| //} |
| |
| void _unhandledSend() { |
| push(new Opaque(hint: "call")); |
| } |
| } |
| |
| /// A listener of parser events that builds summary information as parsing |
| /// progresses. |
| class SummaryBuilder extends StackListener { |
| static int parsed = 0; |
| |
| static int total = 0; |
| |
| /// Whether 'dart:core' was imported explicitly by the current unit. |
| bool isDartCoreImported = false; |
| |
| /// Whether the current unit is part of 'dart:core'. |
| bool isCoreLibrary = false; |
| |
| /// Topmost scope. |
| TopScope topScope; |
| |
| /// Current scope where name references are resolved from. |
| Scope scope; |
| |
| /// Helper to build constant expressions. |
| final ConstExpressionBuilder constBuilder; |
| |
| /// Helper to build initializer expressions. |
| final InitializerBuilder initializerBuilder; |
| |
| /// Whether the current initializer has a type declared. |
| /// |
| /// Because initializers are only used for strong-mode inference, we can skip |
| /// parsing and building initializer expressions when a type is declared. |
| bool typeSeen = false; |
| |
| /// Whether we are currently in the context of a const expression. |
| bool inConstContext = false; |
| |
| /// Uri of the file currently being processed, used for error reporting only. |
| final Uri uri; |
| |
| /// Summaries preassign slots for computed information, this is the next |
| /// available slot. |
| int _slots = 0; |
| |
| UnlinkedParamKind _nextParamKind; |
| |
| SummaryBuilder(Uri uri) |
| : uri = uri, |
| constBuilder = new ConstExpressionBuilder(uri), |
| initializerBuilder = new InitializerBuilder(uri); |
| |
| /// Whether we need to parse the initializer of a declaration. |
| bool get needInitializer => !typeSeen || inConstContext; |
| |
| // Directives: imports, exports, parts |
| |
| /// Assign the next slot. |
| int assignSlot() => ++_slots; |
| |
| void beginClassDeclaration(Token beginToken, Token name) { |
| debugEvent("beginClass"); |
| var classScope = scope = new ClassScope(scope); |
| classScope.className = name.lexeme; |
| } |
| |
| void beginCompilationUnit(Token token) { |
| scope = topScope = new TopScope(); |
| } |
| |
| void beginEnum(Token token) { |
| debugEvent("beginEnum"); |
| scope = new EnumScope(scope); |
| } |
| |
| beginFieldInitializer(Token token) { |
| debugEvent("beginFieldInitializer"); |
| total++; |
| if (needInitializer) { |
| parsed++; |
| if (inConstContext) { |
| push(constBuilder.computeExpression(token.next, scope)); |
| } else { |
| push(initializerBuilder.computeExpression(token.next, scope)); |
| } |
| } |
| } |
| |
| void beginFormalParameters(Token begin) { |
| _nextParamKind = UnlinkedParamKind.required; |
| } |
| |
| void beginFunctionTypeAlias(Token token) { |
| debugEvent('beginFunctionTypeAlias'); |
| // TODO: use a single scope |
| scope = new TypeParameterScope(scope); |
| } |
| |
| beginInitializer(Token token) { |
| // TODO(paulberry): Add support for this. |
| } |
| |
| void beginLiteralString(Token token) { |
| debugEvent("beginLiteralString"); |
| push(token.lexeme.substring(1, token.lexeme.length - 1)); |
| } |
| |
| void beginMember(Token token) { |
| typeSeen = false; |
| inConstContext = false; |
| } |
| |
| // classes, enums, mixins, and typedefs. |
| |
| void beginNamedMixinApplication(Token beginToken, Token name) { |
| debugEvent('beginNamedMixinApplication'); |
| scope = new ClassScope(scope); |
| } |
| |
| void beginOptionalFormalParameters(Token begin) { |
| _nextParamKind = |
| begin == '{' ? UnlinkedParamKind.named : UnlinkedParamKind.positional; |
| } |
| |
| void beginTopLevelMember(Token token) { |
| typeSeen = false; |
| inConstContext = false; |
| } |
| |
| /// If enabled, show a debug message. |
| void debugEvent(String name) { |
| if (const bool.fromEnvironment('DEBUG', defaultValue: false)) { |
| var s = stack.join(' :: '); |
| if (s == '') s = '<empty>'; |
| var bits = 'type?: $typeSeen, const?: $inConstContext'; |
| var prefix = "do $name on:"; |
| prefix = '$prefix${" " * (30 - prefix.length)}'; |
| print('$prefix $bits $s'); |
| } |
| } |
| |
| void endClassBody(int memberCount, Token beginToken, Token endToken) { |
| debugEvent("ClassBody"); |
| } |
| |
| void endClassDeclaration( |
| int interfacesCount, |
| Token beginToken, |
| Token classKeyword, |
| Token extendsKeyword, |
| Token implementsKeyword, |
| Token endToken) { |
| debugEvent("endClassDeclaration"); |
| List<EntityRefBuilder> interfaces = popList(interfacesCount); |
| EntityRef supertype = pop(); |
| List<UnlinkedTypeParamBuilder> typeVariables = pop(); |
| String name = pop(); |
| int modifiers = pop(); |
| List metadata = pop(); |
| checkEmpty(); |
| |
| ClassScope s = scope; |
| s.className = name; |
| s.currentClass |
| ..name = name |
| ..isAbstract = modifiers & _abstract_flag != 0 |
| ..annotations = metadata |
| ..typeParameters = typeVariables |
| ..interfaces = interfaces; |
| if (supertype != null) { |
| s.currentClass.supertype = supertype; |
| } else { |
| s.currentClass.hasNoSupertype = isCoreLibrary && name == 'Object'; |
| } |
| scope = scope.parent; |
| topScope.unit.classes.add(s.currentClass); |
| if (_isPrivate(name)) return; |
| s.publicName |
| ..name = name |
| ..kind = ReferenceKind.classOrEnum |
| ..numTypeParameters = typeVariables?.length; |
| topScope.publicNamespace.names.add(s.publicName); |
| } |
| |
| void endCombinators(int count) { |
| debugEvent("Combinators"); |
| push(popList(count) ?? NullValue.Combinators); |
| } |
| |
| void endCompilationUnit(int count, Token token) { |
| if (!isDartCoreImported) { |
| topScope.unit.imports.add(new UnlinkedImportBuilder(isImplicit: true)); |
| } |
| |
| topScope.expandLazyReferences(); |
| |
| // TODO(sigmund): could this be be optional: done by whoever consumes it? |
| if (const bool.fromEnvironment('SKIP_API')) return; |
| var apiSignature = new ApiSignature(); |
| topScope.unit.collectApiSignature(apiSignature); |
| topScope.unit.apiSignature = apiSignature.toByteList(); |
| } |
| |
| void endConditionalUri(Token ifKeyword, Token equalitySign) { |
| String dottedName = pop(); |
| String value = pop(); |
| String uri = pop(); |
| uri = uri.substring(1, uri.length - 1); |
| push(new UnlinkedConfigurationBuilder( |
| name: dottedName, value: value, uri: uri)); |
| } |
| |
| void endConditionalUris(int count) { |
| push(popList(count) ?? const <UnlinkedConfigurationBuilder>[]); |
| } |
| |
| // members: fields, methods. |
| |
| void endConstructorReference( |
| Token start, Token periodBeforeName, Token endToken) { |
| var ctorName = popIfNotNull(periodBeforeName); |
| var typeArguments = pop(); |
| var className = pop(); |
| push(['ctor-ref:', className, typeArguments, ctorName]); |
| } |
| |
| void endDottedName(count, firstIdentifier) { |
| push(popList(count).join('.')); |
| } |
| |
| void endEnum(Token enumKeyword, Token endBrace, int count) { |
| debugEvent("Enum"); |
| List<String> constants = popList(count); |
| String name = pop(); |
| List metadata = pop(); |
| checkEmpty(); |
| EnumScope s = scope; |
| scope = s.parent; |
| s.currentEnum |
| ..name = name |
| ..annotations = metadata; |
| s.top.unit.enums.add(s.currentEnum); |
| |
| // public namespace: |
| var e = new UnlinkedPublicNameBuilder( |
| name: name, kind: ReferenceKind.classOrEnum, numTypeParameters: 0); |
| for (var s in constants) { |
| e.members.add(new UnlinkedPublicNameBuilder( |
| name: s, kind: ReferenceKind.propertyAccessor, numTypeParameters: 0)); |
| } |
| topScope.publicNamespace.names.add(e); |
| } |
| |
| void endExport(Token exportKeyword, Token semicolon) { |
| debugEvent("Export"); |
| List<UnlinkedCombinator> combinators = pop(); |
| List<UnlinkedConfiguration> conditionalUris = pop(); |
| String uri = pop(); |
| List<UnlinkedExpr> metadata = pop(); |
| topScope.unit.exports |
| .add(new UnlinkedExportNonPublicBuilder(annotations: metadata)); |
| topScope.publicNamespace.exports.add(new UnlinkedExportPublicBuilder( |
| uri: uri, combinators: combinators, configurations: conditionalUris)); |
| checkEmpty(); |
| } |
| |
| void endFactoryMethod( |
| Token beginToken, Token factoryKeyword, Token endToken) { |
| debugEvent("FactoryMethod"); |
| throw new UnimplementedError(); // TODO(paulberry) |
| // pop(); // async-modifiers |
| // /* List<FormalParameterBuilder> formals = */ pop(); |
| // var name = pop(); |
| // /* List<MetadataBuilder> metadata = */ pop(); |
| } |
| |
| void endFieldInitializer(Token assignmentOperator) { |
| debugEvent("FieldInitializer $typeSeen $assignmentOperator"); |
| // This is a variable initializer and it's ignored for now. May also be |
| // constructor initializer. |
| var initializer = |
| needInitializer && assignmentOperator != null ? pop() : null; |
| var name = pop(); |
| push(new _InitializedName( |
| name, new UnlinkedExecutableBuilder(bodyExpr: initializer))); |
| } |
| |
| void endFields( |
| int count, Token covariantKeyword, Token beginToken, Token endToken) { |
| debugEvent("Fields"); |
| var s = scope; |
| if (s is ClassScope) { |
| _endFields(count, s.currentClass.fields, false); |
| } else { |
| throw new UnimplementedError(); // TODO(paulberry): does this ever occur? |
| // _endFields(count, s.currentEnum.values, false); |
| } |
| } |
| |
| void endFormalParameter(Token covariantKeyword, Token thisKeyword, |
| Token nameToken, FormalParameterType kind) { |
| debugEvent("FormalParameter"); |
| // TODO(sigmund): clean up? |
| var nameOrFormal = pop(); |
| if (nameOrFormal is String) { |
| EntityRef type = pop(); |
| pop(); // Modifiers |
| List metadata = pop(); |
| push(new UnlinkedParamBuilder( |
| name: nameOrFormal, |
| kind: _nextParamKind, |
| inheritsCovariantSlot: slotIf(type == null), |
| annotations: metadata, |
| isInitializingFormal: thisKeyword != null, |
| type: type)); |
| } else { |
| push(nameOrFormal); |
| } |
| } |
| |
| void endFormalParameters(int count, Token beginToken, Token endToken) { |
| debugEvent("FormalParameters"); |
| List formals = popList(count); |
| if (formals != null && formals.isNotEmpty) { |
| var last = formals.last; |
| if (last is List) { |
| var newList = new List(formals.length - 1 + last.length); |
| newList.setRange(0, formals.length - 1, formals); |
| newList.setRange(formals.length - 1, newList.length, last); |
| for (int i = 0; i < last.length; i++) { |
| newList[i + formals.length - 1] = last[i]; |
| } |
| formals = newList; |
| } |
| } |
| push(formals ?? NullValue.FormalParameters); |
| } |
| |
| void endFunctionTypeAlias( |
| Token typedefKeyword, Token equals, Token endToken) { |
| debugEvent("endFunctionTypeAlias"); |
| List formals = pop(); |
| List typeVariables = pop(); |
| String name = pop(); |
| EntityRef returnType = pop(); |
| List metadata = pop(); |
| // print('TODO: type alias $name'); |
| checkEmpty(); |
| |
| scope = scope.parent; |
| topScope.unit.typedefs.add(new UnlinkedTypedefBuilder( |
| name: name, |
| typeParameters: typeVariables, |
| returnType: returnType, |
| parameters: formals, |
| annotations: metadata)); |
| |
| _addNameIfPublic(name, ReferenceKind.typedef, typeVariables.length); |
| } |
| |
| void endFunctionTypedFormalParameter( |
| Token covariantKeyword, Token thisKeyword, FormalParameterType kind) { |
| debugEvent("FunctionTypedFormalParameter"); |
| List<UnlinkedParamBuilder> formals = pop(); |
| if (formals != null) formals.forEach((p) => p.inheritsCovariantSlot = null); |
| |
| /* List typeVariables = */ pop(); |
| String name = pop(); |
| EntityRef returnType = pop(); |
| /* int modifiers = */ pop(); |
| List metadata = pop(); |
| |
| push(new UnlinkedParamBuilder( |
| name: name, |
| kind: _nextParamKind, |
| isFunctionTyped: true, |
| parameters: formals, |
| annotations: metadata, |
| type: returnType)); |
| } |
| |
| void endHide(_) { |
| push(new UnlinkedCombinatorBuilder(hides: pop())); |
| } |
| |
| void endIdentifierList(int count) { |
| debugEvent("endIdentifierList"); |
| push(popList(count) ?? NullValue.IdentifierList); |
| } |
| |
| void endImport(Token importKeyword, Token deferredKeyword, Token asKeyword, |
| Token semicolon) { |
| debugEvent("endImport"); |
| List<UnlinkedCombinator> combinators = pop(); |
| String prefix = popIfNotNull(asKeyword); |
| int prefixIndex = |
| prefix == null ? null : topScope.serializeReference(null, prefix); |
| List<UnlinkedConfiguration> conditionalUris = pop(); |
| String uri = pop(); |
| List<UnlinkedExpr> metadata = pop(); // metadata |
| |
| topScope.unit.imports.add(new UnlinkedImportBuilder( |
| uri: uri, |
| prefixReference: prefixIndex, |
| combinators: combinators, |
| configurations: conditionalUris, |
| isDeferred: deferredKeyword != null, |
| annotations: metadata, |
| )); |
| if (uri == 'dart:core') isDartCoreImported = true; |
| checkEmpty(); |
| } |
| |
| void endInitializer(Token assignmentOperator) { |
| // TODO(paulberry): add support for this. |
| debugEvent("Initializer $typeSeen $assignmentOperator"); |
| } |
| |
| void endInitializers(int count, Token beginToken, Token endToken) { |
| debugEvent("Initializers"); |
| // TODO(sigmund): include const-constructor initializers |
| } |
| |
| void endLibraryName(Token libraryKeyword, Token semicolon) { |
| debugEvent("endLibraryName"); |
| String name = pop(); |
| List<UnlinkedExpr> metadata = pop(); // metadata |
| |
| topScope.unit.libraryName = name; |
| topScope.unit.libraryAnnotations = metadata; |
| if (name == 'dart.core') isCoreLibrary = true; |
| } |
| |
| void endLiteralString(int interpolationCount, Token endToken) { |
| assert(interpolationCount == 0); // TODO(sigmund): handle interpolation |
| } |
| |
| void endMember() { |
| debugEvent("Member"); |
| } |
| |
| // TODO(sigmund): handle metadata (this code is incomplete). |
| void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { |
| debugEvent("Metadata"); |
| List arguments = pop(); |
| var result = new UnlinkedExprBuilder(); |
| // If arguments are null, this is an expression, otherwise a constructor |
| // reference. |
| if (arguments == null) { |
| /* String postfix = */ popIfNotNull(periodBeforeName); |
| /* String expression = */ pop(); |
| //push([expression, postfix]); // @x or @p.x |
| } else { |
| /* String name = */ popIfNotNull(periodBeforeName); |
| // TODO(ahe): Type arguments are missing, eventually they should be |
| // available as part of [arguments]. |
| // List<String> typeArguments = null; |
| /* EntityRef typeName = */ pop(); |
| //push([typeName, typeArguments, name, arguments]); |
| } |
| push(result); |
| } |
| |
| void endMetadataStar(int count, bool forParameter) { |
| debugEvent("MetadataStar"); |
| push(popList(count) ?? NullValue.Metadata); |
| } |
| |
| void endMethod(Token getOrSet, Token beginToken, Token endToken) { |
| debugEvent("Method"); |
| int asyncModifier = pop(); |
| List<UnlinkedParam> formals = pop(); |
| List<UnlinkedTypeParamBuilder> typeVariables = pop(); |
| String name = pop(); |
| EntityRef returnType = pop(); |
| int modifiers = pop(); |
| List metadata = pop(); |
| |
| ClassScope s = scope; |
| bool isStatic = modifiers & _static_flag != 0; |
| bool isConst = modifiers & _const_flag != 0; |
| bool isGetter = getOrSet == 'get'; |
| bool isSetter = getOrSet == 'set'; |
| bool isOperator = name == "operator"; // TODO |
| bool isConstructor = |
| name == s.className || name.startsWith('${s.className}.'); |
| |
| if (isConstructor) { |
| name = name == s.className ? '' : name.substring(name.indexOf('.') + 1); |
| } |
| |
| name = isSetter ? '$name=' : name; |
| // Note: we don't include bodies for any method. |
| s.currentClass.executables.add(new UnlinkedExecutableBuilder( |
| name: name, |
| kind: isGetter |
| ? UnlinkedExecutableKind.getter |
| : (isSetter |
| ? UnlinkedExecutableKind.setter |
| : (isConstructor |
| ? UnlinkedExecutableKind.constructor |
| : UnlinkedExecutableKind.functionOrMethod)), |
| isExternal: modifiers & _external_flag != 0, |
| isAbstract: modifiers & _abstract_flag != 0, |
| isAsynchronous: asyncModifier & _async_flag != 0, |
| isGenerator: asyncModifier & _star_flag != 0, |
| isStatic: isStatic, |
| isConst: isConst, |
| constCycleSlot: slotIf(isConst), |
| typeParameters: typeVariables, |
| returnType: returnType, |
| parameters: formals, // TODO: add inferred slot to args |
| annotations: metadata, |
| inferredReturnTypeSlot: |
| slotIf(returnType == null && !isStatic && !isConstructor))); |
| |
| if (isConstructor && name == '') return; |
| if (_isPrivate(name)) return; |
| if (isSetter || isOperator) return; |
| if (!isStatic && !isConstructor) return; |
| s.publicName.members.add(new UnlinkedPublicNameBuilder( |
| name: name, |
| kind: isGetter |
| ? ReferenceKind.propertyAccessor |
| : (isConstructor |
| ? ReferenceKind.constructor |
| : ReferenceKind.method), |
| numTypeParameters: typeVariables.length)); |
| } |
| |
| void endMixinApplication(Token withKeyword) { |
| debugEvent("MixinApplication"); |
| ClassScope s = scope; |
| s.currentClass.mixins = pop(); |
| } |
| |
| void endNamedMixinApplication(Token begin, Token classKeyword, Token equals, |
| Token implementsKeyword, Token endToken) { |
| debugEvent("endNamedMixinApplication"); |
| List<EntityRef> interfaces = popIfNotNull(implementsKeyword); |
| EntityRef supertype = pop(); |
| List typeVariables = pop(); |
| String name = pop(); |
| int modifiers = pop(); |
| List metadata = pop(); |
| // print('TODO: end mix, $name'); |
| checkEmpty(); |
| |
| ClassScope s = scope; |
| s.currentClass |
| ..name = name |
| ..isAbstract = modifiers & _abstract_flag != 0 |
| ..isMixinApplication = true |
| ..annotations = metadata |
| ..typeParameters = typeVariables |
| ..interfaces = interfaces; |
| if (supertype != null) { |
| s.currentClass.supertype = supertype; |
| } else { |
| s.currentClass.hasNoSupertype = isCoreLibrary && name == 'Object'; |
| } |
| scope = scope.parent; |
| topScope.unit.classes.add(s.currentClass); |
| |
| _addNameIfPublic(name, ReferenceKind.classOrEnum, typeVariables.length); |
| } |
| |
| void endOptionalFormalParameters( |
| int count, Token beginToken, Token endToken) { |
| debugEvent("OptionalFormalParameters"); |
| push(popList(count)); |
| } |
| |
| void endPart(Token partKeyword, Token semicolon) { |
| debugEvent("Part"); |
| String uri = pop(); |
| List<UnlinkedExpr> metadata = pop(); |
| topScope.unit.parts.add(new UnlinkedPartBuilder(annotations: metadata)); |
| topScope.publicNamespace.parts.add(uri); |
| checkEmpty(); |
| } |
| |
| void endPartOf(Token partKeyword, Token semicolon, bool hasName) { |
| debugEvent("endPartOf"); |
| String name = pop(); |
| pop(); // metadata |
| topScope.unit.isPartOf = true; |
| if (name == 'dart.core') isCoreLibrary = true; |
| } |
| |
| void endRedirectingFactoryBody(Token beginToken, Token endToken) { |
| debugEvent("RedirectingFactoryBody"); |
| pop(); // Discard ConstructorReferenceBuilder. |
| } |
| |
| void endShow(_) { |
| push(new UnlinkedCombinatorBuilder(shows: pop())); |
| } |
| |
| void endTopLevelFields(int count, Token beginToken, Token endToken) { |
| debugEvent("endTopLevelFields"); |
| _endFields(count, topScope.unit.variables, true); |
| checkEmpty(); |
| } |
| |
| void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) { |
| debugEvent("endTopLevelMethod"); |
| int asyncModifier = pop(); |
| List formals = pop(); |
| List typeVariables = pop(); |
| String name = pop(); |
| EntityRef returnType = pop(); |
| int modifiers = pop(); |
| List metadata = pop(); |
| checkEmpty(); |
| |
| topScope.unit.executables.add(new UnlinkedExecutableBuilder( |
| name: getOrSet == 'set' ? '$name=' : name, |
| kind: getOrSet == 'get' |
| ? UnlinkedExecutableKind.getter |
| : (getOrSet == 'set' |
| ? UnlinkedExecutableKind.setter |
| : UnlinkedExecutableKind.functionOrMethod), |
| isExternal: modifiers & _external_flag != 0, |
| isAbstract: modifiers & _abstract_flag != 0, |
| isAsynchronous: asyncModifier & _async_flag != 0, |
| isGenerator: asyncModifier & _star_flag != 0, |
| isStatic: modifiers & _static_flag != 0, |
| typeParameters: [], // TODO |
| returnType: returnType, |
| parameters: formals, |
| annotations: metadata, |
| inferredReturnTypeSlot: null, // not needed for top-levels |
| // skip body. |
| )); |
| |
| String normalizedName = getOrSet == 'set' ? '$name=' : name; |
| _addNameIfPublic( |
| normalizedName, |
| getOrSet != null |
| ? ReferenceKind.topLevelPropertyAccessor |
| : ReferenceKind.topLevelFunction, |
| typeVariables?.length ?? 0 /* todo */); |
| } |
| |
| void endTypeArguments(int count, Token beginToken, Token endToken) { |
| debugEvent("TypeArguments"); |
| push(popList(count) ?? const []); |
| } |
| |
| void endTypeList(int count) { |
| debugEvent("TypeList"); |
| push(popList(count) ?? NullValue.TypeList); |
| } |
| |
| void endTypeVariable(Token token, Token extendsOrSuper) { |
| debugEvent("endTypeVariable"); |
| EntityRef bound = pop(); |
| String name = pop(); |
| |
| var s = scope; |
| if (s is TypeParameterScope) { |
| s.typeParameters.add(name); |
| } else { |
| throw new UnimplementedError(); // TODO(paulberry) |
| } |
| push(new UnlinkedTypeParamBuilder(name: name, bound: bound)); |
| } |
| |
| void endTypeVariables(int count, Token beginToken, Token endToken) { |
| debugEvent("TypeVariables"); |
| push(popList(count) ?? const []); |
| } |
| |
| void handleAsyncModifier(Token asyncToken, Token starToken) { |
| debugEvent("AsyncModifier"); |
| int asyncModifier = 0; |
| if (asyncToken == "async") asyncModifier |= _async_flag; |
| if (asyncToken == "sync") asyncModifier |= _sync_flag; |
| if (starToken != null) asyncModifier |= _star_flag; |
| push(asyncModifier); |
| } |
| |
| void handleFormalParameterWithoutValue(Token token) { |
| debugEvent("FormalParameterWithoutValue"); |
| } |
| |
| void handleModifier(Token token) { |
| debugEvent("Modifier"); |
| var modifier = _modifierFlag[token.stringValue]; |
| if (modifier & _const_flag != 0) inConstContext = true; |
| push(modifier); |
| } |
| |
| void handleModifiers(int count) { |
| debugEvent("Modifiers"); |
| push((popList(count) ?? const []).fold/*<int>*/(0, (a, b) => a | b)); |
| } |
| |
| void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { |
| debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); |
| } |
| |
| void handleNoFieldInitializer(Token token) { |
| debugEvent("NoFieldInitializer"); |
| push(new _InitializedName(pop(), null)); |
| } |
| |
| void handleNoFunctionBody(Token token) { |
| debugEvent("NoFunctionBody"); |
| // Ignored for now. We shouldn't see any function bodies. |
| } |
| |
| void handleNoInitializers() { |
| debugEvent("NoInitializers"); |
| // This is a constructor initializer and it's ignored for now. |
| } |
| |
| void handleNoTypeVariables(Token token) { |
| debugEvent("NoTypeVariables"); |
| push(const []); |
| } |
| |
| void handleOperatorName(Token operatorKeyword, Token token) { |
| // TODO(sigmund): convert operator names to name used by summaries. |
| debugEvent("OperatorName"); |
| push(operatorKeyword.lexeme); |
| } |
| |
| void handleQualified(Token period) { |
| debugEvent("handleQualified"); |
| String name = pop(); |
| String receiver = pop(); |
| push("$receiver.$name"); |
| } |
| |
| void handleStringPart(token) { |
| debugEvent("handleStringPart"); |
| push(token.lexeme.substring(1, token.lexeme.length - 1)); |
| } |
| |
| void handleType(Token beginToken, Token endToken) { |
| debugEvent("Type"); |
| List<EntityRef> arguments = pop(); |
| String name = pop(); |
| |
| var type; |
| if (name.contains('.')) { |
| var parts = name.split('.'); |
| for (var p in parts) { |
| type = type == null |
| ? new LazyEntityRef(p, scope) |
| : new NestedLazyEntityRef(type, p, scope); |
| } |
| } else { |
| type = new LazyEntityRef(name, scope); |
| } |
| type.typeArguments = arguments; |
| push(type); |
| typeSeen = true; |
| } |
| |
| // helpers to work with the summary format. |
| |
| void handleValuedFormalParameter(Token equals, Token token) { |
| debugEvent("ValuedFormalParameter"); |
| // TODO(sigmund): include default value on optional args. |
| } |
| |
| void handleVoidKeyword(Token token) { |
| debugEvent("VoidKeyword"); |
| // TODO: skip the lazy mechanism |
| push(new LazyEntityRef("void", scope.top)); |
| } |
| |
| /// Assign the next slot if [condition] is true. |
| int slotIf(bool condition) => condition ? assignSlot() : 0; |
| |
| /// Add [name] to the public namespace. |
| void _addName(String name, ReferenceKind kind, {int numTypeParameters: 0}) { |
| topScope.publicNamespace.names.add(new UnlinkedPublicNameBuilder( |
| name: name, kind: kind, numTypeParameters: numTypeParameters)); |
| } |
| |
| /// Add [name] to the public namespace if it's public. |
| void _addNameIfPublic( |
| String name, ReferenceKind kind, int numTypeParameters) { |
| if (_isPrivate(name)) return null; |
| _addName(name, kind, numTypeParameters: numTypeParameters); |
| } |
| |
| /// Add `name` and, if requested, `name=` to the public namespace. |
| void _addPropertyName(String name, {bool includeSetter: false}) { |
| _addName(name, ReferenceKind.topLevelPropertyAccessor); |
| if (includeSetter) { |
| _addName('$name=', ReferenceKind.topLevelPropertyAccessor); |
| } |
| } |
| |
| void _endFields(int count, List result, bool isTopLevel) { |
| debugEvent('EndFields: $count $isTopLevel'); |
| List<_InitializedName> fields = popList(count); |
| EntityRef type = pop(); |
| int modifiers = pop(); |
| List metadata = pop(); |
| |
| bool isStatic = modifiers & _static_flag != 0; |
| bool isFinal = modifiers & _final_flag != 0; |
| bool isConst = modifiers & _const_flag != 0; |
| bool isInstance = !isStatic && !isTopLevel; |
| for (var field in fields) { |
| var name = field.name; |
| var initializer = field.initializer; |
| bool needsPropagatedType = initializer != null && (isFinal || isConst); |
| bool needsInferredType = |
| type == null && (initializer != null || isInstance); |
| result.add(new UnlinkedVariableBuilder( |
| isFinal: isFinal, |
| isConst: isConst, |
| isStatic: isStatic, |
| name: name, |
| type: type, |
| annotations: metadata, |
| initializer: initializer, |
| propagatedTypeSlot: slotIf(needsPropagatedType), |
| inferredTypeSlot: slotIf(needsInferredType))); |
| |
| if (_isPrivate(name)) continue; |
| if (isTopLevel) { |
| _addPropertyName(name, includeSetter: !isFinal && !isConst); |
| } else if (isStatic) { |
| // Any reason setters are not added as well? |
| (scope as ClassScope).publicName.members.add( |
| new UnlinkedPublicNameBuilder( |
| name: name, |
| kind: ReferenceKind.propertyAccessor, |
| numTypeParameters: 0)); |
| } |
| } |
| } |
| |
| /// Whether a name is private and should be excluded from the public |
| /// namespace. |
| bool _isPrivate(String name) => name.startsWith('_'); |
| } |
| |
| /// Internal representation of an initialized name. |
| class _InitializedName { |
| final String name; |
| final UnlinkedExecutableBuilder initializer; |
| _InitializedName(this.name, this.initializer); |
| |
| toString() => "II:" + (initializer != null ? "$name = $initializer" : name); |
| } |