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"; |
| + } |
| +} |