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