Chromium Code Reviews| Index: pkg/analyzer/lib/src/summary/fasta/summary_builder.dart | 
| diff --git a/pkg/analyzer/lib/src/summary/fasta/summary_builder.dart b/pkg/analyzer/lib/src/summary/fasta/summary_builder.dart | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..788238b01b9f0132fe160b9194819aaec247ba3b | 
| --- /dev/null | 
| +++ b/pkg/analyzer/lib/src/summary/fasta/summary_builder.dart | 
| @@ -0,0 +1,1573 @@ | 
| +// 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 'expression_serializer.dart'; | 
| +import 'model.dart'; | 
| +import 'package:front_end/src/fasta/parser/class_member_parser.dart'; | 
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:14
I'm surprised that the sorting of imports doesn't
 
Paul Berry
2017/03/09 23:26:23
Oops--I added these imports in any old location be
 
 | 
| +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 'stack_listener.dart'; | 
| + | 
| +/// 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; | 
| +} | 
| + | 
| +/// A listener of parser events that builds summary information as parsing | 
| +/// progresses. | 
| +class SummaryBuilder extends StackListener { | 
| + /// 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; | 
| + | 
| + /// Whether we need to parse the initializer of a declaration. | 
| + bool get needInitializer => !typeSeen || inConstContext; | 
| + | 
| + /// 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; | 
| + | 
| + SummaryBuilder(Uri uri) | 
| + : uri = uri, | 
| + constBuilder = new ConstExpressionBuilder(uri), | 
| + initializerBuilder = new InitializerBuilder(uri); | 
| + | 
| + void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { | 
| + debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); | 
| + } | 
| + | 
| + void beginCompilationUnit(Token token) { | 
| + scope = topScope = new TopScope(); | 
| + } | 
| + | 
| + 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(); | 
| + } | 
| + | 
| + // Directives: imports, exports, parts | 
| + | 
| + void endHide(_) { | 
| + // ignore: strong_mode_down_cast_composite | 
| + push(new UnlinkedCombinatorBuilder(hides: pop())); | 
| + } | 
| + | 
| + void endShow(_) { | 
| + // ignore: strong_mode_down_cast_composite | 
| + push(new UnlinkedCombinatorBuilder(shows: pop())); | 
| + } | 
| + | 
| + void endCombinators(int count) { | 
| + debugEvent("Combinators"); | 
| + push(popList(count) ?? NullValue.Combinators); | 
| + } | 
| + | 
| + 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>[]); | 
| + } | 
| + | 
| + void endExport(Token exportKeyword, Token semicolon) { | 
| + debugEvent("Export"); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<UnlinkedCombinator> combinators = pop(); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<UnlinkedConfiguration> conditionalUris = pop(); | 
| + String uri = pop(); | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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 endImport(Token importKeyword, Token deferredKeyword, Token asKeyword, | 
| + Token semicolon) { | 
| + debugEvent("endImport"); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<UnlinkedCombinator> combinators = pop(); | 
| + String prefix = popIfNotNull(asKeyword); | 
| + int prefixIndex = prefix == null ? null | 
| + : topScope.serializeReference(null, prefix); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<UnlinkedConfiguration> conditionalUris = pop(); | 
| + String uri = pop(); | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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 endPart(Token partKeyword, Token semicolon) { | 
| + debugEvent("Part"); | 
| + String uri = pop(); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<UnlinkedExpr> metadata = pop(); | 
| + topScope.unit.parts.add(new UnlinkedPartBuilder(annotations: metadata)); | 
| + topScope.publicNamespace.parts.add(uri); | 
| + checkEmpty(); | 
| + } | 
| + | 
| + void endLibraryName(Token libraryKeyword, Token semicolon) { | 
| + debugEvent("endLibraryName"); | 
| + String name = pop(); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<UnlinkedExpr> metadata = pop(); // metadata | 
| + | 
| + topScope.unit.libraryName = name; | 
| + topScope.unit.libraryAnnotations = metadata; | 
| + if (name == 'dart.core') isCoreLibrary = true; | 
| + } | 
| + | 
| + void endPartOf(Token partKeyword, Token semicolon) { | 
| + debugEvent("endPartOf"); | 
| + String name = pop(); | 
| + pop(); // metadata | 
| + topScope.unit.isPartOf = true; | 
| + if (name == 'dart.core') isCoreLibrary = true; | 
| + } | 
| + | 
| + // classes, enums, mixins, and typedefs. | 
| + | 
| + void beginClassDeclaration(Token beginToken, Token name) { | 
| + debugEvent("beginClass"); | 
| + var classScope = scope = new ClassScope(scope); | 
| + classScope.className = name.value; | 
| + } | 
| + | 
| + 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"); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<EntityRefBuilder> interfaces = popList(interfacesCount); | 
| + EntityRef supertype = pop(); | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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 | 
| + // ignore: strong_mode_down_cast_composite | 
| + ..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 beginEnum(Token token) { | 
| + debugEvent("beginEnum"); | 
| + scope = new EnumScope(scope); | 
| + } | 
| + | 
| + void endEnum(Token enumKeyword, Token endBrace, int count) { | 
| + debugEvent("Enum"); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<String> constants = popList(count); | 
| + String name = pop(); | 
| + List metadata = pop(); | 
| + checkEmpty(); | 
| + EnumScope s = scope; | 
| + scope = s.parent; | 
| + s.currentEnum | 
| + ..name = name | 
| + // ignore: strong_mode_down_cast_composite | 
| + ..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 endMixinApplication(Token withKeyword) { | 
| + debugEvent("MixinApplication"); | 
| + ClassScope s = scope; | 
| + // ignore: strong_mode_down_cast_composite | 
| + s.currentClass.mixins = pop(); | 
| + } | 
| + | 
| + void beginNamedMixinApplication(Token beginToken, Token name) { | 
| + debugEvent('beginNamedMixinApplication'); | 
| + scope = new ClassScope(scope); | 
| + } | 
| + | 
| + void endNamedMixinApplication( | 
| + Token begin, Token classKeyword, Token equals, | 
| + Token implementsKeyword, Token endToken) { | 
| + debugEvent("endNamedMixinApplication"); | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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 | 
| + // ignore: strong_mode_down_cast_composite | 
| + ..annotations = metadata | 
| + // ignore: strong_mode_down_cast_composite | 
| + ..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 beginFunctionTypeAlias(Token token) { | 
| + debugEvent('beginFunctionTypeAlias'); | 
| + // TODO: use a single scope | 
| + scope = new TypeParameterScope(scope); | 
| + } | 
| + | 
| + 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, | 
| + // ignore: strong_mode_down_cast_composite | 
| + typeParameters: typeVariables, | 
| + returnType: returnType, | 
| + // ignore: strong_mode_down_cast_composite | 
| + parameters: formals, | 
| + // ignore: strong_mode_down_cast_composite | 
| + annotations: metadata)); | 
| + | 
| + _addNameIfPublic(name, ReferenceKind.typedef, typeVariables.length); | 
| + } | 
| + | 
| + // members: fields, methods. | 
| + | 
| + void beginTopLevelMember(Token token) { | 
| + typeSeen = false; | 
| + inConstContext = false; | 
| + } | 
| + | 
| + void beginMember(Token token) { | 
| + typeSeen = false; | 
| + inConstContext = false; | 
| + } | 
| + | 
| + void endMember() { | 
| + debugEvent("Member"); | 
| + } | 
| + | 
| + void handleType(Token beginToken, Token endToken) { | 
| + debugEvent("Type"); | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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; | 
| + } | 
| + | 
| + void endTypeList(int count) { | 
| + debugEvent("TypeList"); | 
| + push(popList(count) ?? NullValue.TypeList); | 
| + } | 
| + | 
| + static int parsed = 0; | 
| + static int total = 0; | 
| + beginInitializer(Token token) { | 
| + // TODO(paulberry): Add support for this. | 
| + } | 
| + | 
| + beginFieldInitializer(Token token) { | 
| + // TODO(paulberry): Copied from beginFieldInitializer. Is all of this needed? | 
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:13
did you mean from beginInitializer?
 
Paul Berry
2017/03/09 23:26:23
Oops, yes.  I've removed this TODO--it was a place
 
 | 
| + debugEvent("beginFieldInitializer"); | 
| + total++; | 
| + if (needInitializer) { | 
| + parsed++; | 
| + if (inConstContext) { | 
| + push(constBuilder.computeExpression(token.next, scope)); | 
| + } else { | 
| + push(initializerBuilder.computeExpression(token.next, scope)); | 
| + } | 
| + } | 
| + } | 
| + | 
| + void endInitializer(Token assignmentOperator) { | 
| + // TODO(paulberry): add support for this. | 
| + debugEvent("Initializer $typeSeen $assignmentOperator"); | 
| + } | 
| + | 
| + void endFieldInitializer(Token assignmentOperator) { | 
| + // TODO(paulberry): copied from endInitializer. Is all of this needed? | 
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:14
just to make sure I follow - you are wondering if
 
Paul Berry
2017/03/09 23:26:23
At the time I wrote the TODO I didn't understand t
 
 | 
| + 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 endTopLevelFields(int count, Token beginToken, Token endToken) { | 
| + debugEvent("endTopLevelFields"); | 
| + _endFields(count, topScope.unit.variables, true); | 
| + checkEmpty(); | 
| + } | 
| + | 
| + 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? | 
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:14
do you mean that we don't summarize enums altogeth
 
Paul Berry
2017/03/09 23:26:23
We do summarize enums, but the parsing of enums ne
 
 | 
| + // _endFields(count, s.currentEnum.values, false); | 
| + } | 
| + } | 
| + | 
| + void _endFields(int count, List result, bool isTopLevel) { | 
| + debugEvent('EndFields: $count $isTopLevel'); | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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, | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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)); | 
| + } | 
| + } | 
| + } | 
| + | 
| + | 
| + 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, | 
| + // ignore: strong_mode_down_cast_composite | 
| + parameters: formals, | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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 endMethod(Token getOrSet, Token beginToken, Token endToken) { | 
| + debugEvent("Method"); | 
| + int asyncModifier = pop(); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<UnlinkedParam> formals = pop(); | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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 | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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 endTypeArguments(int count, Token beginToken, Token endToken) { | 
| + debugEvent("TypeArguments"); | 
| + push(popList(count) ?? const []); | 
| + } | 
| + | 
| + | 
| + void handleVoidKeyword(Token token) { | 
| + debugEvent("VoidKeyword"); | 
| + // TODO: skip the lazy mechanism | 
| + push(new LazyEntityRef("void", scope.top)); | 
| + } | 
| + | 
| + void endFormalParameter(Token covariantKeyword, Token thisKeyword, 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), | 
| + // ignore: strong_mode_down_cast_composite | 
| + annotations: metadata, | 
| + isInitializingFormal: thisKeyword != null, | 
| + type: type)); | 
| + } else { | 
| + push(nameOrFormal); | 
| + } | 
| + } | 
| + | 
| + // 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 handleStringPart(token) { | 
| + debugEvent("handleStringPart"); | 
| + push(token.value.substring(1, token.value.length - 1)); | 
| + } | 
| + | 
| + void beginLiteralString(Token token) { | 
| + debugEvent("beginLiteralString"); | 
| + push(token.value.substring(1, token.value.length - 1)); | 
| + } | 
| + | 
| + void endLiteralString(int count) { | 
| + assert(count == 0); // TODO(sigmund): handle interpolation | 
| + } | 
| + | 
| + void handleQualified(Token period) { | 
| + debugEvent("handleQualified"); | 
| + String name = pop(); | 
| + String receiver = pop(); | 
| + push("$receiver.$name"); | 
| + } | 
| + | 
| + void endDottedName(count, firstIdentifier) { | 
| + push(popList(count).join('.')); | 
| + } | 
| + | 
| + void handleOperatorName(Token operatorKeyword, Token token) { | 
| + // TODO(sigmund): convert operator names to name used by summaries. | 
| + debugEvent("OperatorName"); | 
| + push(operatorKeyword.value); | 
| + } | 
| + | 
| + void endIdentifierList(int count) { | 
| + debugEvent("endIdentifierList"); | 
| + push(popList(count) ?? NullValue.IdentifierList); | 
| + } | 
| + | 
| + 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(0, (a, b) => a | b)); | 
| + } | 
| + | 
| + UnlinkedParamKind _nextParamKind; | 
| + void beginFormalParameters(Token begin) { | 
| + _nextParamKind = UnlinkedParamKind.required; | 
| + } | 
| + void beginOptionalFormalParameters(Token begin) { | 
| + _nextParamKind = | 
| + begin == '{' ? UnlinkedParamKind.named : UnlinkedParamKind.positional; | 
| + } | 
| + | 
| + void handleValuedFormalParameter(Token equals, Token token) { | 
| + debugEvent("ValuedFormalParameter"); | 
| + // TODO(sigmund): include default value on optional args. | 
| + } | 
| + | 
| + void endFunctionTypedFormalParameter(Token covariantKeyword, Token thisKeyword, FormalParameterType kind) { | 
| + debugEvent("FunctionTypedFormalParameter"); | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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, | 
| + // ignore: strong_mode_down_cast_composite | 
| + annotations: metadata, | 
| + type: returnType)); | 
| + } | 
| + | 
| + void endOptionalFormalParameters( | 
| + int count, Token beginToken, Token endToken) { | 
| + debugEvent("OptionalFormalParameters"); | 
| + push(popList(count)); | 
| + } | 
| + | 
| + 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 endTypeVariables(int count, Token beginToken, Token endToken) { | 
| + debugEvent("TypeVariables"); | 
| + push(popList(count) ?? const []); | 
| + } | 
| + | 
| + 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 endFactoryMethod(Token beginToken, Token endToken) { | 
| + debugEvent("FactoryMethod"); | 
| + throw new UnimplementedError(); // TODO(paulberry) | 
| + // pop(); // async-modifiers | 
| + // /* List<FormalParameterBuilder> formals = */ pop(); | 
| + // var name = pop(); | 
| + // /* List<MetadataBuilder> metadata = */ pop(); | 
| + } | 
| + | 
| + | 
| + void endRedirectingFactoryBody(Token beginToken, Token endToken) { | 
| + debugEvent("RedirectingFactoryBody"); | 
| + pop(); // Discard ConstructorReferenceBuilder. | 
| + } | 
| + | 
| + 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 endInitializers(int count, Token beginToken, Token endToken) { | 
| + debugEvent("Initializers"); | 
| + // TODO(sigmund): include const-constructor initializers | 
| + } | 
| + | 
| + void handleNoFieldInitializer(Token token) { | 
| + debugEvent("NoFieldInitializer"); | 
| + push(new _InitializedName(pop(), null)); | 
| + } | 
| + | 
| + void handleNoTypeVariables(Token token) { | 
| + debugEvent("NoTypeVariables"); | 
| + push(const []); | 
| + } | 
| + | 
| + void handleNoInitializers() { | 
| + debugEvent("NoInitializers"); | 
| + // This is a constructor initializer and it's ignored for now. | 
| + } | 
| + | 
| + void handleNoFunctionBody(Token token) { | 
| + debugEvent("NoFunctionBody"); | 
| + // Ignored for now. We shouldn't see any function bodies. | 
| + } | 
| + | 
| + 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); | 
| + } | 
| + | 
| + // helpers to work with the summary format. | 
| + | 
| + | 
| + /// Assign the next slot. | 
| + int assignSlot() => ++_slots; | 
| + | 
| + /// Assign the next slot if [condition] is true. | 
| + int slotIf(bool condition) => condition ? assignSlot() : 0; | 
| + | 
| + /// Whether a name is private and should be excluded from the public | 
| + /// namespace. | 
| + bool _isPrivate(String name) => name.startsWith('_'); | 
| + | 
| + /// 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] 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` 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); | 
| + } | 
| + } | 
| + | 
| + /// 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 handleFormalParameterWithoutValue(Token token) { | 
| + debugEvent("FormalParameterWithoutValue"); | 
| + } | 
| +} | 
| + | 
| +/// 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); | 
| +} | 
| + | 
| +/// 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. | 
| + Parser get parser; | 
| + | 
| + /// 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; | 
| + | 
| + /// Whether this listener is used to build const expressions. | 
| + bool get forConst => false; | 
| + | 
| + void push(Object o); | 
| + | 
| + 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 handleNoInitializer() {} | 
| + | 
| + void handleIdentifier(Token token, IdentifierContext context) { | 
| + debugEvent("Identifier"); | 
| + if (ignore) return; | 
| + push(new Ref(token.value)); | 
| + } | 
| + | 
| + | 
| + void endFormalParameter(Token covariantKeyword, Token thisKeyword, FormalParameterType kind) { | 
| + debugEvent("FormalParameter"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void endFunctionBody(int count, Token begin, Token end) { | 
| + debugEvent("FunctionBody"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void endFunctionName(Token token) { | 
| + debugEvent("FunctionName"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void endFormalParameters(int c, begin, end) { | 
| + debugEvent("FormalParameters"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void handleAsyncModifier(Token asyncToken, Token starToken) { | 
| + debugEvent("AsyncModifier"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void endReturnStatement(hasValue, Token begin, Token end) { | 
| + debugEvent("ReturnStatement"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void handleStringPart(token) { | 
| + debugEvent("handleStringPart"); | 
| + if (ignore) return; | 
| + push(new StringLiteral(token.value)); | 
| + } | 
| + | 
| + void beginLiteralString(Token token) { | 
| + debugEvent("beginLiteralString"); | 
| + if (ignore) return; | 
| + push(new StringLiteral(token.value)); | 
| + } | 
| + | 
| + void handleStringJuxtaposition(int count) { | 
| + debugEvent("StringJuxtaposition"); | 
| + if (ignore) return; | 
| + popList(count); | 
| + push(new StringLiteral('<juxtapose $count>')); | 
| + } | 
| + | 
| + void endLiteralString(int interpolationCount) { | 
| + debugEvent("endLiteralString"); | 
| + if (interpolationCount != 0) { | 
| + popList(2 * interpolationCount + 1); | 
| + push(new StringLiteral("<interpolate $interpolationCount>")); | 
| + } | 
| + } | 
| + | 
| + void endThrowExpression(throwToken, token) { | 
| + debugEvent("Throw"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void handleNoType(Token token) { | 
| + debugEvent("NoType"); | 
| + if (ignore) return; | 
| + push(NullValue.Type); | 
| + } | 
| + | 
| + void handleNoFormalParameters(Token token) { | 
| + debugEvent("NoFormalParameters"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void handleNoArguments(Token token) { | 
| + debugEvent("NoArguments"); | 
| + if (ignore) return; | 
| + var typeArguments = pop(); | 
| + assert(typeArguments == null); | 
| + push(NullValue.Arguments); | 
| + } | 
| + | 
| + void handleNoFunctionBody(Token token) { | 
| + debugEvent("NoFunctionBody"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void handleNoInitializers() { | 
| + debugEvent("NoInitializers"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void handleNoTypeArguments(Token token) { | 
| + debugEvent("NoTypeArguments"); | 
| + if (ignore) return; | 
| + push(NullValue.TypeArguments); | 
| + } | 
| + | 
| + // type-arguments are expected to be type references passed to constructors | 
| + // and generic methods, we need them to model instantiations. | 
| + void endTypeArguments(int count, Token beginToken, Token endToken) { | 
| + debugEvent("TypeArguments"); | 
| + if (ignore) return; | 
| + push(popList(count) ?? const <TypeRef>[]); | 
| + } | 
| + | 
| + void handleVoidKeyword(Token token) { | 
| + debugEvent("VoidKeyword"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void handleType(Token beginToken, Token endToken) { | 
| + debugEvent("Type"); | 
| + if (ignore) return; | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<TypeRef> arguments = pop(); | 
| + Ref name = pop(); | 
| + push(new TypeRef(name, arguments)); | 
| + } | 
| + | 
| + void endTypeList(int count) { | 
| + debugEvent("TypeList"); | 
| + push(popList(count) ?? const <TypeRef>[]); | 
| + } | 
| + | 
| + 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 handleUnaryPrefixExpression(Token operator) { | 
| + debugEvent("UnaryPrefix"); | 
| + if (ignore) return; | 
| + push(new Unary(pop(), operator.kind)); | 
| + } | 
| + | 
| + void handleLiteralNull(Token token) { | 
| + debugEvent("LiteralNull"); | 
| + if (ignore) return; | 
| + push(new NullLiteral()); | 
| + } | 
| + | 
| + 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)); | 
| + } | 
| + | 
| + void handleLiteralInt(Token token) { | 
| + debugEvent("LiteralInt"); | 
| + if (ignore) return; | 
| + push(new IntLiteral(int.parse(token.value))); | 
| + } | 
| + | 
| + void handleLiteralDouble(Token token) { | 
| + debugEvent("LiteralDouble"); | 
| + if (ignore) return; | 
| + push(new DoubleLiteral(double.parse(token.value))); | 
| + } | 
| + | 
| + void handleLiteralBool(Token token) { | 
| + debugEvent("LiteralBool"); | 
| + if (ignore) return; | 
| + push(new BoolLiteral(token.value == 'true')); | 
| + } | 
| + | 
| + void handleIsOperator(Token operator, Token not, Token endToken) { | 
| + debugEvent("Is"); | 
| + if (ignore) return; | 
| + push(new Is(pop(), pop())); | 
| + } | 
| + | 
| + void handleConstExpression(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(); | 
| + // ignore: strong_mode_down_cast_composite | 
| + push(new ConstCreation(constructorName, positional, named)); | 
| + } | 
| + | 
| + void handleLiteralList(count, begin, constKeyword, end) { | 
| + debugEvent("LiteralList"); | 
| + if (ignore) return; | 
| + var values = popList(count) ?? const <Expression>[]; | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<TypeRef> typeArguments = pop(); | 
| + var type = typeArguments?.single; | 
| + // ignore: strong_mode_down_cast_composite | 
| + push(new ListLiteral(type, values, constKeyword != null)); | 
| + } | 
| + | 
| + void endLiteralMapEntry(colon, token) { | 
| + debugEvent('MapEntry'); | 
| + if (ignore) return; | 
| + var value = pop(); | 
| + var key = pop(); | 
| + push(new KeyValuePair(key, value)); | 
| + } | 
| + | 
| + void handleLiteralMap(count, begin, constKeyword, end) { | 
| + debugEvent('LiteralMap'); | 
| + if (ignore) return; | 
| + var values = popList(count) ?? const <KeyValuePair>[]; | 
| + var typeArgs = pop() ?? const <TypeRef>[]; | 
| + // ignore: strong_mode_down_cast_composite | 
| + push(new MapLiteral(typeArgs, values, constKeyword != null)); | 
| + } | 
| + | 
| + void endLiteralSymbol(token, int dots) { | 
| + debugEvent('LiteralSymbol'); | 
| + if (ignore) return; | 
| + push(new SymbolLiteral(popList(dots).join('.'))); | 
| + } | 
| + | 
| + 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)); | 
| + } | 
| + | 
| + int _withinFunction = 0; | 
| + void beginFunctionDeclaration(token) { | 
| + debugEvent("BeginFunctionDeclaration"); | 
| + _withinFunction++; | 
| + } | 
| + | 
| + // TODO(sigmund): remove | 
| + static const _invariantCheckToken = "invariant check: starting a function"; | 
| + // type-variables are the declared parameters on declarations. | 
| + void handleNoTypeVariables(Token token) { | 
| + debugEvent("NoTypeVariables"); | 
| + if (ignore) return; | 
| + push(_invariantCheckToken); | 
| + } | 
| + | 
| + void endTypeVariable(Token token, Token extendsOrSuper) { | 
| + debugEvent("endTypeVariable"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void endTypeVariables(int count, Token beginToken, Token endToken) { | 
| + debugEvent("TypeVariables"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void beginUnnamedFunction(token) { | 
| + debugEvent("BeginUnnamedFunction"); | 
| + var check = pop(); | 
| + assert(check == _invariantCheckToken); | 
| + _withinFunction++; | 
| + } | 
| + | 
| + void _endFunction(); | 
| + void endFunctionDeclaration(token) { | 
| + debugEvent("FunctionDeclaration"); | 
| + _withinFunction--; | 
| + if (ignore) return; | 
| + _endFunction(); | 
| + } | 
| + void endUnnamedFunction(token) { | 
| + debugEvent("UnnamedFunction"); | 
| + _withinFunction--; | 
| + if (ignore) return; | 
| + _endFunction(); | 
| + } | 
| + | 
| + int _withinCascades = 0; | 
| + void beginCascade(Token token) { | 
| + _withinCascades++; | 
| + } | 
| + | 
| + void endCascade() { | 
| + _withinCascades--; | 
| + throw new UnimplementedError(); // TODO(paulberry): fix the code below. | 
| + // _endCascade(); | 
| + } | 
| + | 
| + void endSend(Token beginToken, Token endToken) { | 
| + debugEvent("EndSend"); | 
| + if (ignore) return; | 
| + // ignore: strong_mode_down_cast_composite | 
| + 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 _unhandledSend(); | 
| + | 
| + void endConstructorReference( | 
| + Token start, Token periodBeforeName, Token endToken) { | 
| + debugEvent("ConstructorReference $start $periodBeforeName"); | 
| + Ref ctorName = popIfNotNull(periodBeforeName); | 
| + assert(ctorName?.prefix == null); | 
| + // ignore: strong_mode_down_cast_composite | 
| + List<TypeRef> typeArgs = pop(); | 
| + Ref type = pop(); | 
| + push(new ConstructorName( | 
| + new TypeRef(type, typeArgs), ctorName?.name)); | 
| + } | 
| + | 
| + void handleModifier(Token token) { | 
| + debugEvent("Modifier"); | 
| + assert(ignore); | 
| + } | 
| + | 
| + void handleModifiers(int count) { | 
| + debugEvent("Modifiers"); | 
| + 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); | 
| + } | 
| + | 
| + // debug helpers | 
| + | 
| + 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 _printExpression(Token token) { | 
| + var current = token; | 
| + var end = new ClassMemberParser(this).skipExpression(current); | 
| + var str = new StringBuffer(); | 
| + while (current != end) { | 
| + if (!["(", ",", ")"].contains(current.value)) str.write(' '); | 
| + str.write(current.value); | 
| + current = current.next; | 
| + } | 
| + print('exp: $str'); | 
| + } | 
| +} | 
| + | 
| +/// Builder for constant expressions. | 
| +/// | 
| +/// Any invalid subexpression is denoted with [Invalid]. | 
| +class ConstExpressionBuilder extends ExpressionListener { | 
| + bool get forConst => true; | 
| + final Uri uri; | 
| + Parser parser; | 
| + ConstExpressionBuilder(this.uri) { | 
| + parser = new Parser(this, | 
| + asyncAwaitKeywordsEnabled: true); | 
| + } | 
| + | 
| + void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { | 
| + debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); | 
| + } | 
| + | 
| + void handleAsOperator(Token op, Token next) { | 
| + debugEvent("As"); | 
| + if (ignore) return; | 
| + push(new As(pop(), pop())); | 
| + } | 
| + | 
| + void handleIndexedExpression(Token openSquareBracket, Token token) { | 
| + debugEvent("Index"); | 
| + if (ignore) return; | 
| + pop(); // receiver | 
| + pop(); // index | 
| + push(new Invalid(hint: "index")); | 
| + } | 
| + | 
| + void handleAssignmentExpression(Token operator) { | 
| + pop(); // lhs | 
| + pop(); // rhs | 
| + push(new Invalid(hint: "assign")); | 
| + } | 
| + | 
| + void handleUnaryPrefixAssignmentExpression(Token operator) { | 
| + pop(); | 
| + push(new Invalid(hint: "prefixOp")); | 
| + } | 
| + | 
| + void handleUnaryPostfixAssignmentExpression(Token operator) { | 
| + pop(); | 
| + push(new Invalid(hint: "postfixOp")); | 
| + } | 
| + | 
| + void handleNamedArgument(colon) { | 
| + debugEvent("NamedArg"); | 
| + if (ignore) return; | 
| + var value = pop(); | 
| + Ref name = pop(); | 
| + push(new NamedArg(name.name, value)); | 
| + } | 
| + | 
| + void endArguments(int count, Token begin, Token end) { | 
| + debugEvent("Arguments"); | 
| + if (ignore) return; | 
| + push(popList(count) ?? const []); | 
| + } | 
| + | 
| + void handleNewExpression(Token token) { | 
| + debugEvent("NewExpression"); | 
| + if (ignore) return; | 
| + pop(); // args | 
| + pop(); // ctor | 
| + push(new Invalid(hint: "new")); | 
| + } | 
| + | 
| + void _endFunction() { | 
| + assert(_withinFunction >= 0); | 
| + push(new Invalid(hint: 'function')); | 
| + } | 
| + | 
| + // TODO(paulberry): is this needed? | 
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:14
really depends on how we want to handle recovery o
 
Paul Berry
2017/03/09 23:26:23
We *do* expect to run the summary builder on inval
 
 | 
| + //void _endCascade() { | 
| + // push(new Invalid(hint: 'cascades')); | 
| + //} | 
| + | 
| + void _unhandledSend() { | 
| + push(new Invalid(hint: "call")); | 
| + } | 
| +} | 
| + | 
| +/// 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; | 
| + | 
| + InitializerBuilder(this.uri) { | 
| + parser = new Parser(this, | 
| + asyncAwaitKeywordsEnabled: true); | 
| + } | 
| + | 
| + void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { | 
| + debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); | 
| + } | 
| + | 
| + bool get ignore => super.ignore || _inArguments > 0; | 
| + | 
| + void handleAsOperator(Token op, Token next) { | 
| + debugEvent("As"); | 
| + if (ignore) return; | 
| + TypeRef type = pop(); | 
| + pop(); | 
| + push(new Opaque(type: type)); | 
| + } | 
| + | 
| + // Not necessary, but we don't use the value, so we can abstract it: | 
| + void handleIsOperator(Token operator, Token not, Token endToken) { | 
| + debugEvent("Is"); | 
| + if (ignore) return; | 
| + throw new UnimplementedError(); // TODO(paulberry): fix the code below. | 
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:13
is the fix related to references in general?
 
Paul Berry
2017/03/09 23:26:23
I don't know what the proper fix is.  The reason I
 
 | 
| + // push(new Opaque(type: new TypeRef(new Ref('bool')))); | 
| + } | 
| + | 
| + void handleIndexedExpression(Token openSquareBracket, Token token) { | 
| + debugEvent("Index"); | 
| + if (ignore) return; | 
| + pop(); | 
| + pop(); | 
| + push(new Opaque()); | 
| + } | 
| + | 
| + 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 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 handleUnaryPostfixAssignmentExpression(Token operator) { | 
| + debugEvent("PostFix"); | 
| + if (ignore) return; | 
| + // the post-fix effect is not visible to the enclosing expression | 
| + push(new OpaqueOp(pop())); | 
| + } | 
| + | 
| + int _inArguments = 0; | 
| + void beginArguments(Token token) { | 
| + // TODO(sigmund): determine if we can ignore arguments. | 
| + //_inArguments++; | 
| + } | 
| + | 
| + void handleNamedArgument(colon) { | 
| + debugEvent("NamedArg"); | 
| + if (ignore) return; | 
| + pop(); | 
| + pop(); | 
| + push(NullValue.Arguments); | 
| + } | 
| + | 
| + void endArguments(int count, Token begin, Token end) { | 
| + debugEvent("Arguments"); | 
| + //_inArguments--; | 
| + if (ignore) return; | 
| + push(popList(count) ?? const []); | 
| + //push([new Opaque(hint: "arguments")]); | 
| + } | 
| + | 
| + void handleNewExpression(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 _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")); | 
| + } | 
| +} | 
| + | 
| +// bit-masks to encode modifiers as bits on an int. | 
| + | 
| +/// 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, | 
| +}; | 
| + | 
| +const _var_flag = 0; | 
| +const _final_flag = 1; | 
| +const _const_flag = 1 << 1; | 
| +const _abstract_flag = 1 << 2; | 
| +const _static_flag = 1 << 3; | 
| +const _external_flag = 1 << 4; | 
| + | 
| +// bit-masks to encode async modifiers as bits on an int. | 
| + | 
| +const _async_flag = 1; | 
| +const _sync_flag = 1 << 1; | 
| +const _star_flag = 1 << 2; | 
| + | 
| +/// 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"; | 
| + } | 
| +} |