Index: pkg/fasta/lib/src/kernel/body_builder.dart |
diff --git a/pkg/fasta/lib/src/kernel/body_builder.dart b/pkg/fasta/lib/src/kernel/body_builder.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e2a702c484b555e56e9b39d8f885cd480785bb8d |
--- /dev/null |
+++ b/pkg/fasta/lib/src/kernel/body_builder.dart |
@@ -0,0 +1,2487 @@ |
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library fasta.body_builder; |
+ |
+import 'package:dart_parser/src/parser.dart' show |
+ FormalParameterType, |
+ optional; |
+ |
+import 'package:dart_parser/src/error_kind.dart' show |
+ ErrorKind; |
+ |
+import 'package:kernel/ast.dart'; |
+ |
+import 'package:kernel/clone.dart' show |
+ CloneVisitor; |
+ |
+import 'package:kernel/transformations/flags.dart' show |
+ TransformerFlag; |
+ |
+import 'package:kernel/class_hierarchy.dart' show |
+ ClassHierarchy; |
+ |
+import 'package:kernel/core_types.dart' show |
+ CoreTypes; |
+ |
+import 'package:dart_scanner/src/token.dart' show |
+ BeginGroupToken, |
+ ErrorToken, |
+ Token, |
+ isBinaryOperator, |
+ isMinusOperator; |
+ |
+import '../errors.dart' show |
+ InputError, |
+ internalError; |
+ |
+import '../errors.dart' as errors show |
+ inputError; |
+ |
+import '../source/scope_listener.dart' show |
+ JumpTargetKind, |
+ NullValue, |
+ ScopeListener; |
+ |
+import '../builder/scope.dart' show |
+ AccessErrorBuilder, |
+ AmbiguousBuilder, |
+ Scope; |
+ |
+import '../source/outline_builder.dart' show |
+ asyncMarkerFromTokens; |
+ |
+import 'builder_accessors.dart'; |
+ |
+import 'frontend_accessors.dart' show |
+ buildIsNull, |
+ makeBinary, |
+ makeLet; |
+ |
+import 'builder_accessors.dart' as builder_accessors show |
+ throwNoSuchMethodError; |
+ |
+import '../quote.dart' show |
+ Quote, |
+ analyzeQuote, |
+ unescape, |
+ unescapeFirstStringPart, |
+ unescapeLastStringPart, |
+ unescapeString; |
+ |
+import '../modifier.dart' show |
+ Modifier, |
+ constMask, |
+ finalMask; |
+ |
+import 'redirecting_factory_body.dart' show |
+ getRedirectionTarget; |
+ |
+import 'kernel_builder.dart'; |
+ |
+const bool showNits = false; |
+ |
+class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper { |
+ final KernelLibraryBuilder library; |
+ |
+ final MemberBuilder member; |
+ |
+ final KernelClassBuilder classBuilder; |
+ |
+ final ClassHierarchy hierarchy; |
+ |
+ final CoreTypes coreTypes; |
+ |
+ final bool isInstanceMember; |
+ |
+ final Map<String, FieldInitializer> fieldInitializers = |
+ <String, FieldInitializer>{}; |
+ |
+ final Scope enclosingScope; |
+ |
+ Scope formalParameterScope; |
+ |
+ bool isFirstIdentifier = false; |
+ |
+ bool hasParserError = false; |
+ |
+ bool inInitializer = false; |
+ |
+ bool inCatchClause = false; |
+ |
+ int functionNestingLevel = 0; |
+ |
+ Statement compileTimeErrorInTry; |
+ |
+ Statement compileTimeErrorInLoopOrSwitch; |
+ |
+ Scope switchScope; |
+ |
+ CloneVisitor cloner; |
+ |
+ BodyBuilder(this.library, this.member, Scope scope, this.formalParameterScope, |
+ this.hierarchy, this.coreTypes, this.classBuilder, this.isInstanceMember) |
+ : enclosingScope = scope, |
+ super(scope); |
+ |
+ bool get inConstructor { |
+ return functionNestingLevel == 0 && member is KernelConstructorBuilder; |
+ } |
+ |
+ bool get isInstanceContext { |
+ return isInstanceMember || member is KernelConstructorBuilder; |
+ } |
+ |
+ void push(Object node) { |
+ isFirstIdentifier = false; |
+ inInitializer = false; |
+ super.push(node); |
+ } |
+ |
+ Expression popForValue() => toValue(pop()); |
+ |
+ Expression popForEffect() => toEffect(pop()); |
+ |
+ Expression toValue(Object node) { |
+ if (node is UnresolvedIdentifier) { |
+ return throwNoSuchMethodError(node.name.name, new Arguments.empty(), |
+ node.fileOffset, isGetter: true); |
+ } else if (node is BuilderAccessor) { |
+ return node.buildSimpleRead(); |
+ } else if (node is TypeVariableBuilder) { |
+ TypeParameterType type = node.buildTypesWithBuiltArguments(null); |
+ if (!isInstanceContext && type.parameter.parent is Class) { |
+ return buildCompileTimeError( |
+ "Type variables can only be used in instance methods."); |
+ } else { |
+ return new TypeLiteral(type); |
+ } |
+ } else if (node is TypeDeclarationBuilder) { |
+ return new TypeLiteral(node.buildTypesWithBuiltArguments(null)); |
+ } else if (node is KernelTypeBuilder) { |
+ return new TypeLiteral(node.build()); |
+ } else if (node is Expression) { |
+ return node; |
+ } else if (node is PrefixBuilder) { |
+ return buildCompileTimeError("A library can't be used as an expression."); |
+ } else { |
+ return internalError("Unhandled: ${node.runtimeType}"); |
+ } |
+ } |
+ |
+ Expression toEffect(Object node) { |
+ if (node is BuilderAccessor) return node.buildForEffect(); |
+ return toValue(node); |
+ } |
+ |
+ List<Expression> popListForValue(int n) { |
+ List<Expression> list = |
+ new List<Expression>.filled(n, null, growable: true); |
asgerf
2017/01/13 18:39:06
I think the recommended way of doing this is <Expr
ahe
2017/01/16 08:32:09
Who makes this recommendation?
asgerf
2017/01/16 11:52:26
It is here:
https://api.dartlang.org/stable/1.21.1
ahe
2017/01/16 12:32:38
I think I disagree with the advice when it comes t
|
+ for (int i = n - 1; i >= 0; i--) { |
+ list[i] = popForValue(); |
+ } |
+ return list; |
+ } |
+ |
+ List<Expression> popListForEffect(int n) { |
+ List<Expression> list = |
+ new List<Expression>.filled(n, null, growable: true); |
+ for (int i = n - 1; i >= 0; i--) { |
+ list[i] = popForEffect(); |
+ } |
+ return list; |
+ } |
+ |
+ Block popBlock(int count) { |
+ List<Statement> statements = popList(count) ?? <Statement>[]; |
+ List<Statement> copy; |
+ for (int i = 0; i < statements.length; i++) { |
+ var statement = statements[i]; |
+ if (statement is List) { |
+ if (copy == null) { |
+ copy = new List<Statement>.from(statements.getRange(0, i)); |
asgerf
2017/01/13 18:39:07
Maybe use ??= here
ahe
2017/01/16 08:32:09
Done.
|
+ } |
+ copy.addAll(statement); |
+ } else if (copy != null) { |
+ copy.add(statement); |
+ } |
+ } |
+ return new Block(copy ?? statements); |
+ } |
+ |
+ Statement popStatementIfNotNull(Object value) { |
+ return value == null ? null : popStatement(); |
+ } |
+ |
+ Statement popStatement() { |
+ var statement = pop(); |
+ if (statement is List) { |
+ return new Block(new List<Statement>.from(statement)); |
+ } else { |
+ return statement; |
+ } |
+ } |
+ |
+ void ignore(Unhandled value) { |
+ pop(); |
+ } |
+ |
+ void enterSwitchScope() { |
+ push(switchScope ?? NullValue.SwitchScope); |
+ switchScope = scope; |
+ } |
+ |
+ void exitSwitchScope() { |
+ switchScope = pop(); |
+ } |
+ |
+ Uri get uri => library.fileUri ?? library.uri; |
+ |
+ JumpTarget createJumpTarget(JumpTargetKind kind) => new JumpTarget(kind); |
+ |
+ void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { |
+ debugEvent("Metadata"); |
+ pop(); // Arguments. |
+ popIfNotNull(periodBeforeName); // Postfix. |
+ pop(); // Type arguments. |
+ pop(); // Expression or type name (depends on arguments). |
+ // TODO(ahe): Implement metadata on local declarations. |
+ } |
+ |
+ void endMetadataStar(int count, bool forParameter) { |
+ debugEvent("MetadataStar"); |
+ push(NullValue.Metadata); |
+ } |
+ |
+ void endTopLevelFields(int count, Token beginToken, Token endToken) { |
+ debugEvent("TopLevelFields"); |
+ doFields(count); |
+ } |
+ |
+ void endFields(int count, Token beginToken, Token endToken) { |
+ debugEvent("Fields"); |
+ doFields(count); |
+ pop(); // Metadata. |
+ } |
+ |
+ void doFields(int count) { |
+ List nodes = popList(count); |
+ pop(); // Type. |
+ pop(); // Modifiers. |
+ for (var node in nodes) { |
+ if (node is Identifier) { |
+ // Ignore, there's no initializer. |
+ } else if (node is VariableDeclaration) { |
+ FieldBuilder field; |
+ if (classBuilder != null) { |
+ field = classBuilder.members[node.name]; |
asgerf
2017/01/13 18:39:07
What if a setter has the same name as a final fiel
ahe
2017/01/16 08:32:09
Added an internal error and TODO.
|
+ } else { |
+ field = library.members[node.name]; |
+ } |
+ field.initializer = node.initializer; |
+ } else { |
+ internalError("Unhandled: ${node.runtimeType}"); |
+ } |
+ } |
+ } |
+ |
+ void endMember() { |
+ debugEvent("Member"); |
+ checkEmpty(); |
+ } |
+ |
+ void endFunctionBody(int count, Token beginToken, Token endToken) { |
+ debugEvent("FunctionBody"); |
+ if (beginToken == null) { |
+ assert(count == 0); |
+ push(NullValue.Block); |
+ } else { |
+ Block block = popBlock(count); |
+ exitLocalScope(); |
+ push(block); |
+ } |
+ } |
+ |
+ void prepareInitializers() { |
asgerf
2017/01/13 18:39:06
It's not clear if this is part of the event listen
ahe
2017/01/16 08:32:09
I like the idea of @override, and I could potentia
ahe
2017/01/18 11:51:44
Followed up in CL 2645513002.
|
+ scope = formalParameterScope; |
+ assert(fieldInitializers.isEmpty); |
+ final member = this.member; |
+ if (member is KernelConstructorBuilder) { |
+ Constructor constructor = member.constructor; |
+ classBuilder.members.forEach((String name, Builder builder) { |
+ if (builder is KernelFieldBuilder && builder.isInstanceMember) { |
+ // TODO(ahe): Compute initializers (as in `field = initializer`). |
+ fieldInitializers[name] = new FieldInitializer(builder.field, null) |
+ ..parent = constructor; |
+ } |
+ }); |
+ if (member.formals != null) { |
+ for (KernelFormalParameterBuilder formal in member.formals) { |
+ if (formal.hasThis) { |
+ FieldInitializer initializer = fieldInitializers[formal.name]; |
+ if (initializer != null) { |
+ fieldInitializers.remove(formal.name); |
+ initializer.value = new VariableGet(formal.declaration) |
+ ..parent = initializer; |
+ member.addInitializer(initializer); |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ void beginConstructorInitializer(Token token) { |
+ debugEvent("beginConstructorInitializer"); |
+ inInitializer = true; |
+ } |
+ |
+ void endConstructorInitializer(Token token) { |
+ debugEvent("endConstructorInitializer"); |
+ assert(!inInitializer); |
+ final member = this.member; |
+ var node = pop(); |
+ Initializer initializer; |
+ if (node is Initializer) { |
+ initializer = node; |
+ } else if (node is BuilderAccessor) { |
+ initializer = node.buildFieldInitializer(fieldInitializers); |
+ } else if (node is ConstructorInvocation) { |
+ initializer = new SuperInitializer(node.target, node.arguments); |
+ } else { |
+ if (node is !Throw) { |
+ node = wrapInvalid(node); |
+ } |
+ initializer = |
+ new LocalInitializer(new VariableDeclaration.forValue(node)); |
+ } |
+ if (member is KernelConstructorBuilder) { |
+ member.addInitializer(initializer); |
+ } else { |
+ inputError("Can't have initializers: ${member.name}", token.charOffset); |
+ } |
+ } |
+ |
+ void handleNoInitializers() { |
+ debugEvent("NoInitializers"); |
+ } |
+ |
+ void endInitializers(int count, Token beginToken, Token endToken) { |
+ debugEvent("Initializers"); |
+ } |
+ |
+ void finishFunction(FormalParameters formals, |
+ AsyncMarker asyncModifier, Statement body) { |
+ debugEvent("finishFunction"); |
+ KernelFunctionBuilder builder = member; |
+ if (builder is KernelConstructorBuilder) { |
+ if (asyncModifier != AsyncMarker.Sync) { |
+ // TODO(ahe): Change this to a null check. |
+ inputError("Can't be marked as ${asyncModifier}: ${builder.name}", |
+ body?.fileOffset); |
+ } |
+ } else if (builder is KernelProcedureBuilder) { |
+ builder.asyncModifier = asyncModifier; |
+ } else { |
+ internalError("Unhandled: ${builder.runtimeType}"); |
+ } |
+ builder.body = body; |
+ if (formals?.optional != null) { |
+ Iterator<FormalParameterBuilder> formalBuilders = |
+ builder.formals.skip(formals.required.length).iterator; |
+ for (VariableDeclaration parameter in formals.optional.formals) { |
+ bool hasMore = formalBuilders.moveNext(); |
+ assert(hasMore); |
+ VariableDeclaration realParameter = formalBuilders.current.target; |
+ Expression initializer = parameter.initializer ?? new NullLiteral(); |
+ realParameter.initializer = initializer |
+ ..parent = realParameter; |
+ } |
+ } |
+ } |
+ |
+ void endExpressionStatement(Token token) { |
+ debugEvent("ExpressionStatement"); |
+ push(new ExpressionStatement(popForEffect())); |
+ } |
+ |
+ void endArguments(int count, Token beginToken, Token endToken) { |
+ debugEvent("Arguments"); |
+ List arguments = popList(count) ?? <Expression>[]; |
+ int firstNamedArgument = arguments.length; |
+ for (int i = 0; i < arguments.length; i++) { |
+ var node = arguments[i]; |
+ if (node is NamedExpression) { |
+ firstNamedArgument = i < firstNamedArgument ? i : firstNamedArgument; |
+ } else { |
+ arguments[i] = node = toValue(node); |
+ if (i > firstNamedArgument) { |
+ internalError("Expected named argument: $node"); |
+ } |
+ } |
+ } |
+ if (firstNamedArgument < arguments.length) { |
+ List<Expression> positional = new List<Expression>.from( |
+ arguments.getRange(0, firstNamedArgument)); |
+ List<NamedExpression> named = new List<NamedExpression>.from( |
+ arguments.getRange(firstNamedArgument,arguments.length)); |
+ push(new Arguments(positional, named: named)); |
+ } else { |
+ push(new Arguments(arguments)); |
+ } |
+ } |
+ |
+ void handleParenthesizedExpression(BeginGroupToken token) { |
+ debugEvent("ParenthesizedExpression"); |
+ push(popForValue()); |
+ } |
+ |
+ void endSend(Token token) { |
+ debugEvent("Send"); |
+ Arguments arguments = pop(); |
+ List typeArguments = pop(); |
+ Object receiver = pop(); |
+ if (arguments != null && typeArguments != null) { |
+ arguments.types.addAll(typeArguments); |
+ } else { |
+ assert(typeArguments == null); |
+ } |
+ if (receiver is Identifier) { |
+ Name name = new Name(receiver.name, library.library); |
+ if (arguments == null) { |
+ push(new IncompletePropertyAccessor(this, token.charOffset, name)); |
+ } else { |
+ push(new SendAccessor(this, token.charOffset, name, arguments)); |
+ } |
+ } else if (arguments == null) { |
+ push(receiver); |
+ } else { |
+ push(finishSend(receiver, arguments, token.charOffset)); |
+ } |
+ } |
+ |
+ finishSend(Object receiver, Arguments arguments, int charOffset) { |
+ if (receiver is BuilderAccessor) { |
+ return receiver.doInvocation(charOffset, arguments); |
+ } else if (receiver is UnresolvedIdentifier) { |
+ return throwNoSuchMethodError(receiver.name.name, arguments, |
+ receiver.fileOffset); |
+ } else { |
+ return buildMethodInvocation(toValue(receiver), new Name("call"), |
asgerf
2017/01/13 18:39:07
The Name object should be put in a final static fi
ahe
2017/01/16 08:32:09
Done.
|
+ arguments, charOffset); |
+ } |
+ } |
+ |
+ void beginCascade(Token token) { |
+ debugEvent("beginCascade"); |
+ Expression expression = popForValue(); |
+ if (expression is CascadeReceiver) { |
+ push(expression); |
+ push(new VariableAccessor( |
+ this, expression.fileOffset, expression.variable)); |
+ expression.extend(); |
+ } else { |
+ VariableDeclaration variable = |
+ new VariableDeclaration.forValue(expression); |
+ push(new CascadeReceiver(variable)); |
+ push(new VariableAccessor(this, expression.fileOffset, variable)); |
+ } |
+ } |
+ |
+ void endCascade() { |
+ debugEvent("endCascade"); |
+ Expression expression = popForEffect(); |
+ CascadeReceiver cascadeReceiver = pop(); |
+ cascadeReceiver.finalize(expression); |
+ push(cascadeReceiver); |
+ } |
+ |
+ void handleBinaryExpression(Token token) { |
+ debugEvent("BinaryExpression"); |
+ if (optional(".", token) || optional("..", token)) { |
+ return doDotOrCascadeExpression(token); |
+ } |
+ if (optional("&&", token) || optional("||", token)) { |
+ return doLogicalExpression(token); |
+ } |
+ if (optional("??", token)) return doIfNull(token); |
+ if (optional("?.", token)) return doIfNotNull(token); |
+ Expression argument = popForValue(); |
+ var receiver = pop(); |
+ bool isSuper = false; |
+ if (receiver is ThisAccessor && receiver.isSuper) { |
+ isSuper = true; |
+ receiver = new ThisExpression(); |
+ } |
+ push(buildBinaryOperator(toValue(receiver), token, argument, isSuper)); |
+ } |
+ |
+ Expression buildBinaryOperator(Expression a, Token token, Expression b, |
+ bool isSuper) { |
+ bool negate = false; |
+ String operator = token.stringValue; |
+ if (identical("!=", operator)) { |
+ operator = "=="; |
+ negate = true; |
+ } |
+ if (!isBinaryOperator(operator) && !isMinusOperator(operator)) { |
+ return buildCompileTimeError("Not an operator: '$operator'.", |
+ token.charOffset); |
+ } else { |
+ Expression result = makeBinary(a, new Name(operator), null, b); |
+ if (isSuper) { |
+ result = toSuperMethodInvocation(result); |
+ } |
+ return negate ? new Not(result) : result; |
+ } |
+ } |
+ |
+ void doLogicalExpression(Token token) { |
+ Expression argument = popForValue(); |
+ Expression receiver = popForValue(); |
+ push(new LogicalExpression(receiver, token.stringValue, argument)); |
+ } |
+ |
+ /// Handle `a ?? b`. |
+ void doIfNull(Token token) { |
+ Expression b = popForValue(); |
+ Expression a = popForValue(); |
+ VariableDeclaration variable = new VariableDeclaration.forValue(a); |
+ push(makeLet(variable, |
+ new ConditionalExpression(buildIsNull(new VariableGet(variable)), |
+ b, new VariableGet(variable), const DynamicType()))); |
+ } |
+ |
+ /// Handle `a?.b(...)`. |
+ void doIfNotNull(Token token) { |
+ IncompleteSend send = pop(); |
+ push(send.withReceiver(pop(), isNullAware: true)); |
+ } |
+ |
+ void doDotOrCascadeExpression(Token token) { |
+ // TODO(ahe): Handle null-aware. |
+ IncompleteSend send = pop(); |
+ Object receiver = optional(".", token) ? pop() : popForValue(); |
+ push(send.withReceiver(receiver)); |
+ } |
+ |
+ Expression toSuperMethodInvocation(MethodInvocation node) { |
+ Member target = lookupSuperMember(node.name); |
+ bool isNoSuchMethod = target == null; |
+ if (target is Procedure) { |
+ if (!target.isAccessor) { |
+ if (areArgumentsCompatible(target.function, node.arguments)) { |
+ // TODO(ahe): Use [DirectMethodInvocation] when possible. |
+ Expression result = new DirectMethodInvocation(new ThisExpression(), |
+ target, node.arguments); |
+ result = new SuperMethodInvocation(node.name, node.arguments, null); |
+ return result; |
+ } else { |
+ isNoSuchMethod = true; |
+ } |
+ } |
+ } |
+ if (isNoSuchMethod) { |
+ return throwNoSuchMethodError( |
+ node.name.name, node.arguments, node.fileOffset, isSuper: true); |
+ } |
+ // TODO(ahe): Use [DirectPropertyGet] when possible. |
+ Expression receiver = new DirectPropertyGet(new ThisExpression(), target); |
+ receiver = new SuperPropertyGet(node.name, target); |
+ return buildMethodInvocation(receiver, new Name("call"), node.arguments, |
+ node.fileOffset); |
+ } |
+ |
+ bool areArgumentsCompatible(FunctionNode function, Arguments arguments) { |
+ // TODO(ahe): Implement this. |
+ return true; |
+ } |
+ |
+ Expression throwNoSuchMethodError(String name, Arguments arguments, |
+ int charOffset, {bool isSuper: false, isGetter: false, isSetter: false}) { |
+ return builder_accessors.throwNoSuchMethodError(name, arguments, uri, |
+ charOffset, coreTypes, isSuper: isSuper, isGetter: isGetter, |
+ isSetter: isSetter); |
+ } |
+ |
+ MapLiteral buildMapLiteral(Map map) { |
asgerf
2017/01/13 18:39:06
buildConstantStringMapLiteral?
The current name w
ahe
2017/01/16 08:32:09
It's not used anymore.
|
+ List<MapEntry> entries = <MapEntry>[]; |
+ map.forEach((key, value) { |
+ entries.add( |
+ new MapEntry(new StringLiteral("$key"), new StringLiteral("$value"))); |
+ }); |
+ return new MapLiteral(entries, isConst: true); |
+ } |
+ |
+ Member lookupSuperMember(Name name, {bool isSetter: false}) { |
+ Class superclass = classBuilder.cls.superclass; |
+ return superclass == null |
+ ? null |
+ : hierarchy.getDispatchTarget(superclass, name, setter: isSetter); |
+ } |
+ |
+ Constructor lookupConstructor(Name name, {bool isSuper}) { |
+ Class cls = classBuilder.cls; |
+ if (isSuper) { |
+ cls = cls.superclass; |
+ while (cls.isMixinApplication) { |
+ cls = cls.superclass; |
+ } |
+ } |
+ if (cls != null) { |
+ for (Constructor constructor in cls.constructors) { |
+ if (constructor.name == name) return constructor; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ void beginExpression(Token token) { |
+ debugEvent("beginExpression"); |
+ isFirstIdentifier = true; |
+ } |
+ |
+ Builder computeSetter(Builder builder, Scope scope, String name) { |
+ if (builder.isSetter) return builder; |
+ if (builder.isGetter) return scope.lookupSetter(name); |
+ return builder.isField ? (builder.isFinal ? null : builder) : null; |
+ } |
+ |
+ void handleIdentifier(Token token) { |
+ debugEvent("handleIdentifier"); |
+ String name = token.value; |
+ if (isFirstIdentifier) { |
+ assert(!inInitializer || this.scope == enclosingScope || |
+ this.scope.parent == enclosingScope); |
+ // This deals with this kind of initializer: `C(a) : a = a;` |
+ Scope scope = inInitializer ? enclosingScope : this.scope; |
+ Builder builder = scope.lookup(name); |
+ push(builderToFirstExpression(builder, name, token.charOffset)); |
+ } else { |
+ push(new Identifier(name)..fileOffset = token.charOffset); |
+ } |
+ } |
+ |
+ builderToFirstExpression(Builder builder, String name, int charOffset, |
+ {bool isPrefix: false}) { |
+ if (builder == null || (!isInstanceContext && builder.isInstanceMember)) { |
+ if (!isPrefix && identical(name, "dynamic") && builder == null) { |
+ return new KernelInterfaceTypeBuilder(name, null); |
+ } |
+ Name n = new Name(name, library.library); |
+ if (!isPrefix && isInstanceContext) { |
+ assert(builder == null); |
+ return new ThisPropertyAccessor(this, charOffset, n, null, null); |
+ } else { |
+ return new UnresolvedIdentifier(n) |
+ ..fileOffset = charOffset; |
+ } |
+ } else if (builder.isTypeDeclaration) { |
+ return builder; |
+ } else if (builder.isLocal) { |
+ return new VariableAccessor(this, charOffset, builder.target); |
+ } else if (builder.isInstanceMember) { |
+ return new ThisPropertyAccessor(this, charOffset, |
+ new Name(name, library.library), null, null); |
+ } else if (builder.isRegularMethod) { |
+ assert(builder.isStatic || builder.isTopLevel); |
+ return new StaticAccessor(this, charOffset, builder.target, null); |
+ } else if (builder is PrefixBuilder) { |
+ return builder; |
+ } else if (builder is MixedAccessor) { |
+ return new StaticAccessor(this, charOffset, builder.getter.target, |
+ builder.setter.target); |
+ } else { |
+ if (builder is AccessErrorBuilder) { |
+ AccessErrorBuilder error = builder; |
+ builder = error.builder; |
+ } |
+ if (builder.target == null) { |
+ return internalError("Unhandled: ${builder}"); |
+ } |
+ Member getter = builder.target.hasGetter ? builder.target : null; |
+ Member setter = builder.target.hasSetter ? builder.target : null; |
+ setter ??= computeSetter(builder, scope, name)?.target; |
+ return |
+ new StaticAccessor(this, charOffset, getter, setter); |
+ } |
+ } |
+ |
+ void handleQualified(Token period) { |
+ debugEvent("Qualified"); |
+ Identifier name = pop(); |
+ var receiver = pop(); |
+ push([receiver, name]); |
+ } |
+ |
+ void beginLiteralString(Token token) { |
+ debugEvent("beginLiteralString"); |
+ push(token); |
+ } |
+ |
+ void handleStringPart(Token token) { |
+ debugEvent("StringPart"); |
+ push(token); |
+ } |
+ |
+ void endLiteralString(int interpolationCount) { |
+ debugEvent("endLiteralString"); |
+ if (interpolationCount == 0) { |
+ Token token = pop(); |
+ push(new StringLiteral(unescapeString(token.value))); |
+ } else { |
+ List parts = popList(1 + interpolationCount * 2); |
+ Token first = parts.first; |
+ Token last = parts.last; |
+ Quote quote = analyzeQuote(first.value); |
+ List<Expression> expressions = <Expression>[]; |
+ expressions.add(new StringLiteral(unescapeFirstStringPart( |
+ first.value, quote))); |
+ for (int i = 1; i < parts.length - 1; i++) { |
+ var part = parts[i]; |
+ if (part is Token) { |
+ expressions.add(new StringLiteral(unescape(part.value, quote))); |
+ } else { |
+ expressions.add(toValue(part)); |
+ } |
+ } |
+ expressions.add( |
+ new StringLiteral(unescapeLastStringPart(last.value, quote))); |
+ push(new StringConcatenation(expressions)); |
+ } |
+ } |
+ |
+ void handleStringJuxtaposition(int literalCount) { |
+ debugEvent("StringJuxtaposition"); |
+ List<Expression> parts = popListForValue(literalCount); |
+ List<Expression> expressions; |
+ // Flatten string juxtapositions of string interpolation. |
+ for (int i = 0; i < parts.length; i++) { |
+ Expression part = parts[i]; |
+ if (part is StringConcatenation) { |
+ if (expressions == null) { |
+ expressions = parts.sublist(0, i); |
+ } |
+ expressions.addAll(part.expressions); |
+ } else { |
+ if (expressions != null) { |
+ expressions.add(part); |
+ } |
+ } |
+ } |
+ push(new StringConcatenation(expressions ?? parts)); |
+ } |
+ |
+ void handleLiteralInt(Token token) { |
+ debugEvent("LiteralInt"); |
+ push(new IntLiteral(int.parse(token.value))); |
+ } |
+ |
+ void endReturnStatement( |
+ bool hasExpression, Token beginToken, Token endToken) { |
+ debugEvent("ReturnStatement"); |
+ Expression expression = hasExpression ? popForValue() : null; |
+ if (expression != null && inConstructor) { |
+ push(buildCompileTimeErrorStatement("Can't return from a constructor.", |
+ beginToken.charOffset)); |
+ } else { |
+ push(new ReturnStatement(expression)); |
+ } |
+ } |
+ |
+ void endIfStatement(Token ifToken, Token elseToken) { |
+ Statement elsePart = popStatementIfNotNull(elseToken); |
+ Statement thenPart = popStatement(); |
+ Expression condition = popForValue(); |
+ push(new IfStatement(condition, thenPart, elsePart)); |
+ } |
+ |
+ void endInitializer(Token assignmentOperator) { |
+ debugEvent("Initializer"); |
+ assert(assignmentOperator.stringValue == "="); |
+ Expression initializer = popForValue(); |
+ Identifier identifier = pop(); |
+ push(new VariableDeclaration(identifier.name, initializer: initializer)); |
+ } |
+ |
+ void endInitializedIdentifier() { |
+ // TODO(ahe): Use [InitializedIdentifier] here? |
+ debugEvent("InitializedIdentifier"); |
+ TreeNode node = pop(); |
+ VariableDeclaration variable; |
+ if (node is VariableDeclaration) { |
+ variable = node; |
+ } else if (node is Identifier) { |
+ variable = new VariableDeclaration(node.name); |
+ } else { |
+ internalError("unhandled identifier: ${node.runtimeType}"); |
+ } |
+ push(variable); |
+ scope[variable.name] = new KernelVariableBuilder(variable); |
+ } |
+ |
+ void endVariablesDeclaration(int count, Token endToken) { |
+ debugEvent("VariablesDeclaration"); |
+ List<VariableDeclaration> variables = popList(count); |
+ DartType type = pop(); |
+ int modifiers = Modifier.validate(pop()); |
+ bool isConst = (modifiers & constMask) != 0; |
+ bool isFinal = (modifiers & finalMask) != 0; |
+ if (type != null || isConst || isFinal) { |
+ type ??= const DynamicType(); |
+ for (VariableDeclaration variable in variables) { |
+ variable |
+ ..type = type |
+ ..isConst = isConst |
+ ..isFinal = isFinal; |
+ } |
+ } |
+ if (variables.length != 1) { |
+ push(variables); |
+ } else { |
+ push(variables.single); |
+ } |
+ } |
+ |
+ void endBlock(int count, Token beginToken, Token endToken) { |
+ debugEvent("Block"); |
+ Block block = popBlock(count); |
+ exitLocalScope(); |
+ push(block); |
+ } |
+ |
+ void handleAssignmentExpression(Token token) { |
+ debugEvent("AssignmentExpression"); |
+ Expression value = popForValue(); |
+ var accessor = pop(); |
+ if (accessor is TypeDeclarationBuilder) { |
+ push(wrapInvalid( |
+ new TypeLiteral(accessor.buildTypesWithBuiltArguments(null)))); |
+ } else if (accessor is! BuilderAccessor) { |
+ push(buildCompileTimeError("Can't assign to this.", token.charOffset)); |
+ } else { |
+ push(new DelayedAssignment(this, token.charOffset, accessor, value, |
+ token.stringValue)); |
+ } |
+ } |
+ |
+ void enterLoop() { |
+ if (peek() is LabelTarget) { |
+ LabelTarget target = peek(); |
+ enterBreakTarget(target.breakTarget); |
+ enterContinueTarget(target.continueTarget); |
+ } else{ |
+ enterBreakTarget(); |
+ enterContinueTarget(); |
+ } |
+ } |
+ |
+ void exitLoopOrSwitch(Statement statement) { |
+ if (compileTimeErrorInLoopOrSwitch != null) { |
+ push(compileTimeErrorInLoopOrSwitch); |
+ compileTimeErrorInLoopOrSwitch = null; |
+ } else { |
+ push(statement); |
+ } |
+ } |
+ |
+ void endForStatement( |
+ int updateExpressionCount, Token beginToken, Token endToken) { |
+ debugEvent("ForStatement"); |
+ Statement body = popStatement(); |
+ List<Expression> updates = popListForEffect(updateExpressionCount); |
+ Statement conditionStatement = popStatement(); |
+ Expression condition = null; |
+ if (conditionStatement is ExpressionStatement) { |
+ condition = conditionStatement.expression; |
+ } else { |
+ assert(conditionStatement is EmptyStatement); |
+ } |
+ List<VariableDeclaration> variables = <VariableDeclaration>[]; |
+ var variableOrExpression = pop(); |
+ Statement begin; |
+ if (variableOrExpression is BuilderAccessor) { |
+ variableOrExpression = variableOrExpression.buildForEffect(); |
+ } |
+ if (variableOrExpression is VariableDeclaration) { |
+ variables.add(variableOrExpression); |
+ } else if (variableOrExpression is List) { |
+ variables.addAll(variableOrExpression); |
+ } else if (variableOrExpression == null) { |
+ // Do nothing. |
+ } else if (variableOrExpression is Expression) { |
+ begin = new ExpressionStatement(variableOrExpression); |
+ } else { |
+ return internalError("Unhandled: ${variableOrExpression.runtimeType}"); |
+ } |
+ exitLocalScope(); |
+ JumpTarget continueTarget = exitContinueTarget(); |
+ JumpTarget breakTarget = exitBreakTarget(); |
+ if (continueTarget.hasUsers) { |
+ body = new LabeledStatement(body); |
+ continueTarget.resolveContinues(body); |
+ } |
+ Statement result = new ForStatement(variables, condition, updates, body); |
+ if (begin != null) { |
+ result = new Block(<Statement>[begin, result]); |
+ } |
+ if (breakTarget.hasUsers) { |
+ result = new LabeledStatement(result); |
+ breakTarget.resolveBreaks(result); |
+ } |
+ exitLoopOrSwitch(result); |
+ } |
+ |
+ void endAwaitExpression(Token beginToken, Token endToken) { |
+ debugEvent("AwaitExpression"); |
+ push(new AwaitExpression(popForValue())); |
+ } |
+ |
+ void handleAsyncModifier(Token asyncToken, Token starToken) { |
+ debugEvent("AsyncModifier"); |
+ push(asyncMarkerFromTokens(asyncToken, starToken)); |
+ } |
+ |
+ void handleLiteralList( |
+ int count, Token beginToken, Token constKeyword, Token endToken) { |
+ debugEvent("LiteralList"); |
+ List<Expression> expressions = popListForValue(count); |
+ List<DartType> typeArguments = pop(); |
+ DartType typeArgument = const DynamicType(); |
+ if (typeArguments != null) { |
+ typeArgument = typeArguments.first; |
+ if (typeArguments.length > 1) { |
+ typeArgument = const DynamicType(); |
+ warning("Too many type arguments on List literal.", |
+ beginToken.charOffset); |
+ } |
+ } |
+ push(new ListLiteral(expressions, typeArgument: typeArgument, |
+ isConst: constKeyword != null)); |
+ } |
+ |
+ void handleLiteralBool(Token token) { |
+ debugEvent("LiteralBool"); |
+ bool value = optional("true", token); |
+ assert(value || optional("false", token)); |
+ push(new BoolLiteral(value)); |
+ } |
+ |
+ void handleLiteralDouble(Token token) { |
+ debugEvent("LiteralDouble"); |
+ push(new DoubleLiteral(double.parse(token.value))); |
+ } |
+ |
+ void handleLiteralNull(Token token) { |
+ debugEvent("LiteralNull"); |
+ push(new NullLiteral()); |
+ } |
+ |
+ void handleLiteralMap( |
+ int count, Token beginToken, Token constKeyword, Token endToken) { |
+ debugEvent("LiteralMap"); |
+ List<MapEntry> entries = popList(count) ?? <MapEntry>[]; |
+ List<DartType> typeArguments = pop(); |
+ DartType keyType = const DynamicType(); |
+ DartType valueType = const DynamicType(); |
+ if (typeArguments != null) { |
+ if (typeArguments.length != 2) { |
+ keyType = const DynamicType(); |
+ valueType = const DynamicType(); |
+ warning("Map literal requires two type arguments.", |
+ beginToken.charOffset); |
+ } else { |
+ keyType = typeArguments[0]; |
+ valueType = typeArguments[0]; |
asgerf
2017/01/13 18:39:06
Should be typeArguments[1]
ahe
2017/01/16 08:32:09
Done.
|
+ } |
+ } |
+ push(new MapLiteral(entries, keyType: keyType, valueType: valueType, |
+ isConst: constKeyword != null)); |
+ } |
+ |
+ void endLiteralMapEntry(Token colon, Token endToken) { |
+ debugEvent("LiteralMapEntry"); |
+ Expression value = popForValue(); |
+ Expression key = popForValue(); |
+ push(new MapEntry(key, value)); |
+ } |
+ |
+ void beginLiteralSymbol(Token token) { |
+ isFirstIdentifier = false; |
+ } |
+ |
+ String symbolPartToString(name) { |
+ if (name is Identifier) { |
+ return name.name; |
+ } else if (name is Operator) { |
+ return name.name; |
+ } else { |
+ return internalError("Unhandled: ${name.runtimeType}"); |
+ } |
+ } |
+ |
+ void endLiteralSymbol(Token hashToken, int identifierCount) { |
+ debugEvent("LiteralSymbol"); |
+ String value; |
+ if (identifierCount == 1) { |
+ value = symbolPartToString(popForValue()); |
+ } else { |
+ List parts = popList(identifierCount); |
+ value = symbolPartToString(parts.first); |
+ for (int i = 1; i < parts.length; i++) { |
+ value += ".${symbolPartToString(parts[i])}"; |
+ } |
+ } |
+ push(new SymbolLiteral(value)); |
+ } |
+ |
+ DartType toKernelType(String name, List<DartType> arguments) { |
+ if (identical(name, "void")) return const VoidType(); |
+ if (identical(name, "dynamic")) return const DynamicType(); |
+ Builder builder = scope.lookup(name); |
+ if (builder is TypeDeclarationBuilder) { |
+ return builder.buildTypesWithBuiltArguments(arguments); |
+ } |
+ if (builder == null) { |
+ print("$uri: Type not found: $name"); |
+ } else { |
+ print("$uri: Not a type: $name"); |
+ } |
+ // TODO(ahe): Create an error somehow. |
+ return const DynamicType(); |
+ } |
+ |
+ void endType(Token beginToken, Token endToken) { |
+ // TODO(ahe): The scope is wrong for return types of generic functions. |
+ debugEvent("Type"); |
+ List<DartType> arguments = pop(); |
+ var name = pop(); |
+ if (name is List) { |
+ if (name.length != 2) { |
+ return internalError("Unexpected: $name.length"); |
+ } |
+ var prefix = name[0]; |
+ if (prefix is Identifier) { |
+ prefix = prefix.name; |
+ } |
+ var suffix = name[1]; |
+ if (suffix is Identifier) { |
+ suffix = suffix.name; |
+ } |
+ Builder builder; |
+ if (prefix is Builder) { |
+ builder = prefix; |
+ } else { |
+ builder = scope.lookup(prefix); |
+ } |
+ if (builder is PrefixBuilder) { |
+ name = builder.exports[suffix]; |
+ } else { |
+ return inputError( |
+ "Can't be used as a type: '${debugName(prefix, suffix)}'.", |
+ beginToken.charOffset); |
+ } |
+ } |
+ if (name is Identifier) { |
+ name = name.name; |
+ } |
+ if (name is BuilderAccessor) { |
+ warning("'${beginToken.value}' isn't a type.", beginToken.charOffset); |
+ push(const DynamicType()); |
+ } else if (name is UnresolvedIdentifier) { |
+ warning("'${name.name}' isn't a type.", beginToken.charOffset); |
+ push(const DynamicType()); |
+ } else if (name is TypeVariableBuilder) { |
+ push(name.buildTypesWithBuiltArguments(arguments)); |
+ } else if (name is TypeDeclarationBuilder) { |
+ push(name.buildTypesWithBuiltArguments(arguments)); |
+ } else if (name is TypeBuilder) { |
+ push(name.build()); |
+ } else { |
+ push(toKernelType(name, arguments)); |
+ } |
+ if (peek() is TypeParameterType) { |
+ TypeParameterType type = peek(); |
+ if (!isInstanceContext && type.parameter.parent is Class) { |
+ pop(); |
+ warning("Type variables can only be used in instance methods.", |
+ beginToken.charOffset); |
+ push(const DynamicType()); |
+ } |
+ } |
+ } |
+ |
+ void handleVoidKeyword(Token token) { |
+ debugEvent("VoidKeyword"); |
+ push(const VoidType()); |
+ } |
+ |
+ void handleAsOperator(Token operator, Token endToken) { |
+ debugEvent("AsOperator"); |
+ DartType type = pop(); |
+ Expression expression = popForValue(); |
+ push(new AsExpression(expression, type)); |
+ } |
+ |
+ void handleIsOperator(Token operator, Token not, Token endToken) { |
+ debugEvent("IsOperator"); |
+ DartType type = pop(); |
+ Expression expression = popForValue(); |
+ expression = new IsExpression(expression, type); |
+ if (not != null) { |
+ expression = new Not(expression); |
+ } |
+ push(expression); |
+ } |
+ |
+ void handleConditionalExpression(Token question, Token colon) { |
+ debugEvent("ConditionalExpression"); |
+ Expression elseExpression = popForValue(); |
+ Expression thenExpression = popForValue(); |
+ Expression condition = popForValue(); |
+ push(new ConditionalExpression( |
+ condition, thenExpression, elseExpression, const DynamicType())); |
+ } |
+ |
+ void endThrowExpression(Token throwToken, Token endToken) { |
+ debugEvent("ThrowExpression"); |
+ Expression expression = popForValue(); |
+ push(new Throw(expression)); |
+ } |
+ |
+ void endFormalParameter(Token thisKeyword) { |
+ debugEvent("FormalParameter"); |
+ if (thisKeyword != null) { |
+ if (!inConstructor) { |
+ return inputError("'this' parameters can only be used on constructors.", |
+ thisKeyword.charOffset); |
+ } |
+ } |
+ Identifier name = pop(); |
+ DartType type = pop(); |
+ pop(); // Modifiers. |
+ ignore(Unhandled.Metadata); |
+ VariableDeclaration variable; |
+ if (!inCatchClause && functionNestingLevel == 0) { |
+ var builder = formalParameterScope.lookup(name.name); |
+ if (builder == null) { |
+ return inputError("'${name.name}' isn't a field in this class.", |
+ name.fileOffset); |
+ } |
+ if (thisKeyword == null) { |
+ variable = builder.build(); |
+ variable.initializer = name.initializer; |
+ } else if (builder.isField && builder.parent == classBuilder) { |
+ FieldBuilder field = builder; |
+ if (type != null) { |
+ nit("Ignoring type on 'this' parameter '${name.name}'.", |
+ name.fileOffset); |
+ } |
+ type = field.target.type ?? const DynamicType(); |
+ variable = new VariableDeclaration(name.name, type: type, |
+ initializer: name.initializer); |
+ } else { |
+ return inputError("'${name.name}' isn't a field in this class.", |
+ name.fileOffset); |
+ } |
+ } else { |
+ variable = new VariableDeclaration(name.name, |
+ type: type ?? const DynamicType(), initializer: name.initializer); |
+ } |
+ push(variable); |
+ } |
+ |
+ void endOptionalFormalParameters( |
+ int count, Token beginToken, Token endToken) { |
+ debugEvent("OptionalFormalParameters"); |
+ FormalParameterType kind = optional("{", beginToken) |
+ ? FormalParameterType.NAMED : FormalParameterType.POSITIONAL; |
+ push(new OptionalFormals(kind, popList(count))); |
+ } |
+ |
+ void beginFunctionTypedFormalParameter(Token token) { |
+ debugEvent("beginFunctionTypedFormalParameter"); |
+ functionNestingLevel++; |
+ } |
+ |
+ void handleFunctionTypedFormalParameter(Token token) { |
+ debugEvent("FunctionTypedFormalParameter"); |
+ if (inCatchClause || functionNestingLevel != 0) { |
+ exitLocalScope(); |
+ } |
+ FormalParameters formals = pop(); |
+ ignore(Unhandled.TypeVariables); |
+ Identifier name = pop(); |
+ DartType returnType = pop(); |
+ push(formals.toFunctionType(returnType)); |
+ push(name); |
+ functionNestingLevel--; |
+ } |
+ |
+ void handleValuedFormalParameter(Token equals, Token token) { |
+ debugEvent("ValuedFormalParameter"); |
+ Expression initializer = popForValue(); |
+ Identifier name = pop(); |
+ push(new InitializedIdentifier(name.name, initializer)); |
+ } |
+ |
+ void endFormalParameters(int count, Token beginToken, Token endToken) { |
+ debugEvent("FormalParameters"); |
+ OptionalFormals optional; |
+ if (count > 0 && peek() is OptionalFormals) { |
+ optional = pop(); |
+ count--; |
+ } |
+ FormalParameters formals = new FormalParameters( |
+ popList(count) ?? <VariableDeclaration>[], optional); |
+ push(formals); |
+ if (inCatchClause || functionNestingLevel != 0) { |
+ enterLocalScope(formals.computeFormalParameterScope(scope)); |
+ } |
+ } |
+ |
+ void beginCatchClause(Token token) { |
+ debugEvent("beginCatchClause"); |
+ inCatchClause = true; |
+ } |
+ |
+ void endCatchClause(Token token) { |
+ debugEvent("CatchClause"); |
+ inCatchClause = false; |
+ } |
+ |
+ void handleCatchBlock(Token onKeyword, Token catchKeyword) { |
+ debugEvent("CatchBlock"); |
+ Block body = pop(); |
+ if (catchKeyword != null) { |
+ exitLocalScope(); |
+ } |
+ FormalParameters catchParameters = popIfNotNull(catchKeyword); |
+ DartType type = popIfNotNull(onKeyword) ?? const DynamicType(); |
+ VariableDeclaration exception; |
+ VariableDeclaration stackTrace; |
+ if (catchParameters != null) { |
+ if (catchParameters.required.length > 0) { |
+ exception = catchParameters.required[0]; |
+ } |
+ if (catchParameters.required.length > 1) { |
+ stackTrace = catchParameters.required[1]; |
+ } |
+ if (catchParameters.required.length > 2 || |
+ catchParameters.optional != null) { |
+ body = new Block(<Statement>[new InvalidStatement()]); |
+ compileTimeErrorInTry ??= buildCompileTimeErrorStatement( |
+ "Invalid catch arguments.", catchKeyword.next.charOffset); |
+ } |
+ } |
+ push(new Catch(exception, body, guard: type, stackTrace: stackTrace)); |
+ } |
+ |
+ void endTryStatement( |
+ int catchCount, Token tryKeyword, Token finallyKeyword) { |
+ Statement finallyBlock = popStatementIfNotNull(finallyKeyword); |
+ List<Catch> catches = popList(catchCount); |
+ Statement tryBlock = popStatement(); |
+ if (compileTimeErrorInTry == null) { |
+ if (catches != null) { |
+ tryBlock = new TryCatch(tryBlock, catches); |
+ } |
+ if (finallyBlock != null) { |
+ tryBlock = new TryFinally(tryBlock, finallyBlock); |
+ } |
+ push(tryBlock); |
+ } else { |
+ push(compileTimeErrorInTry); |
+ compileTimeErrorInTry = null; |
+ } |
+ } |
+ |
+ void handleNoExpression(Token token) { |
+ debugEvent("NoExpression"); |
+ push(NullValue.Expression); |
+ } |
+ |
+ void handleIndexedExpression( |
+ Token openCurlyBracket, Token closeCurlyBracket) { |
+ debugEvent("IndexedExpression"); |
+ Expression index = popForValue(); |
+ Expression receiver = popForValue(); |
+ push(IndexAccessor.make(this, openCurlyBracket.charOffset, receiver, index, |
+ null, null)); |
+ } |
+ |
+ void handleUnaryPrefixExpression(Token token) { |
+ debugEvent("UnaryPrefixExpression"); |
+ Expression expression = popForValue(); |
+ if (optional("!", token)) { |
+ push(new Not(expression)); |
+ } else { |
+ String operator = token.stringValue; |
+ if (optional("-", token)) { |
+ operator = "unary-"; |
+ } |
+ push(buildMethodInvocation(expression, new Name(operator), |
+ new Arguments.empty(), token.charOffset)); |
+ } |
+ } |
+ |
+ Name incrementOperator(Token token) { |
+ if (optional("++", token)) return new Name("+"); |
+ if (optional("--", token)) return new Name("-"); |
+ return internalError("Unknown increment operator: ${token.value}"); |
+ } |
+ |
+ void handleUnaryPrefixAssignmentExpression(Token token) { |
+ debugEvent("UnaryPrefixAssignmentExpression"); |
+ var accessor = pop(); |
+ if (accessor is BuilderAccessor) { |
+ push(accessor.buildPrefixIncrement(incrementOperator(token))); |
+ } else { |
+ push(wrapInvalid(toValue(accessor))); |
+ } |
+ } |
+ |
+ void handleUnaryPostfixAssignmentExpression(Token token) { |
+ debugEvent("UnaryPostfixAssignmentExpression"); |
+ var accessor = pop(); |
+ if (accessor is BuilderAccessor) { |
+ push(new DelayedPostfixIncrement(this, token.charOffset, accessor, |
+ incrementOperator(token), null)); |
+ } else { |
+ push(wrapInvalid(toValue(accessor))); |
+ } |
+ } |
+ |
+ void endConstructorReference( |
+ Token start, Token periodBeforeName, Token endToken) { |
+ debugEvent("ConstructorReference"); |
+ // A constructor reference can contain up to three identifiers: |
+ // |
+ // a) type <type-arguments>? |
+ // b) type <type-arguments>? . name |
+ // c) prefix . type <type-arguments>? |
+ // d) prefix . type <type-arguments>? . name |
+ // |
+ // This isn't a legal constructor reference: |
+ // |
+ // type . name <type-arguments> |
+ // |
+ // But the parser can't tell this from type c) above. |
+ // |
+ // This method pops 2 (or 3 if periodBeforeName != null) values from the |
+ // stack and pushes 3 values: a type, a list of type arguments, and a name. |
+ // |
+ // If the constructor reference can be resolved, type is either a |
+ // ClassBuilder, or a ThisPropertyAccessor. Otherwise, it's an error that |
+ // should be handled later. |
+ Identifier suffix = popIfNotNull(periodBeforeName); |
+ Identifier identifier; |
+ List<DartType> typeArguments = pop(); |
+ var type = pop(); |
+ if (type is List) { |
+ var prefix = type[0]; |
+ identifier = type[1]; |
+ if (prefix is PrefixBuilder) { |
+ // TODO(ahe): Handle privacy in prefix.exports. |
+ type = builderToFirstExpression( |
+ prefix.exports[identifier.name], identifier.name, start.charOffset); |
+ identifier = null; |
+ } else if (prefix is ClassBuilder) { |
+ type = prefix; |
+ } else { |
+ type = new Identifier(start.value)..fileOffset = start.charOffset; |
+ } |
+ } |
+ String name; |
+ if (identifier != null && suffix != null) { |
+ name = "${identifier.name}.${suffix.name}"; |
+ } else if (identifier != null) { |
+ name = identifier.name; |
+ } else if (suffix != null) { |
+ name = suffix.name; |
+ } else { |
+ name = ""; |
+ } |
+ push(type); |
+ push(typeArguments ?? NullValue.TypeArguments); |
+ push(name); |
+ } |
+ |
+ Expression buildStaticInvocation(Member target, Arguments arguments, |
+ {bool isConst: false}) { |
+ List<TypeParameter> typeParameters = target.function.typeParameters; |
+ if (target is Constructor) { |
+ typeParameters = target.enclosingClass.typeParameters; |
+ } |
+ if (!addDefaultArguments(target.function, arguments, typeParameters)) { |
+ return throwNoSuchMethodError(target.name.name, arguments, -1); |
+ } |
+ if (target is Constructor) { |
+ return new ConstructorInvocation(target, arguments) |
+ ..isConst = isConst; |
+ } else { |
+ return new StaticInvocation(target, arguments) |
+ ..isConst = isConst; |
+ } |
+ } |
+ |
+ bool addDefaultArguments(FunctionNode function, Arguments arguments, |
+ List<TypeParameter> typeParameters) { |
+ bool missingInitializers = false; |
+ |
+ Expression defaultArgumentFrom(Expression expression) { |
+ if (expression == null) { |
+ missingInitializers = true; |
+ return null; |
+ } |
+ cloner ??= new CloneVisitor(); |
+ return cloner.clone(expression); |
+ } |
+ |
+ if (arguments.positional.length < function.requiredParameterCount || |
+ arguments.positional.length > function.positionalParameters.length) { |
+ return false; |
+ } |
+ for (int i = arguments.positional.length; i < function.positionalParameters.length; i++) { |
+ var expression = defaultArgumentFrom(function.positionalParameters[i].initializer); |
asgerf
2017/01/13 18:39:06
Long line
ahe
2017/01/16 08:32:09
Done.
|
+ expression?.parent = arguments; |
+ arguments.positional.add(expression); |
+ } |
+ Map<String, VariableDeclaration> names; |
+ if (function.namedParameters.isNotEmpty) { |
+ names = <String, VariableDeclaration>{}; |
+ for (VariableDeclaration parameter in function.namedParameters) { |
+ names[parameter.name] = parameter; |
+ } |
+ } |
+ if (arguments.named.isNotEmpty) { |
+ if (names == null) return false; |
+ for (NamedExpression argument in arguments.named) { |
+ VariableDeclaration parameter = names.remove(argument.name); |
+ if (parameter == null) { |
+ return false; |
+ } |
+ } |
+ } |
+ if (names != null) { |
+ for (String name in names.keys) { |
+ VariableDeclaration parameter = names[name]; |
+ arguments.named.add( |
+ new NamedExpression( |
+ name, defaultArgumentFrom(parameter.initializer)) |
+ ..parent = arguments); |
+ } |
+ } |
+ if (typeParameters.length != arguments.types.length) { |
+ arguments.types.clear(); |
+ for (int i = 0; i < typeParameters.length; i++) { |
+ arguments.types.add(const DynamicType()); |
+ } |
+ } |
+ |
+ if (missingInitializers) { |
+ library.addArgumentsWithMissingDefaultValues(arguments, function); |
+ } |
+ return true; |
+ } |
+ |
+ void handleNewExpression(Token token) { |
+ debugEvent("NewExpression"); |
+ Arguments arguments = pop(); |
+ String name = pop(); |
+ List typeArguments = pop(); |
+ var type = pop(); |
+ |
+ if (typeArguments != null) { |
+ assert(arguments.types.isEmpty); |
+ arguments.types.addAll(typeArguments); |
+ } |
+ |
+ String errorName; |
+ if (type is ClassBuilder) { |
+ Builder b = type.findConstructorOrFactory(name); |
+ Member target; |
+ if (b == null) { |
+ // Not found. Reported below. |
+ } else if (b.isConstructor) { |
+ if (type.cls.isAbstract) { |
+ // TODO(ahe): Generate abstract instantiation error. |
+ } else { |
+ target = b.target; |
+ } |
+ } else if (b.isFactory) { |
+ target = getRedirectionTarget(b.target); |
+ if (target == null) { |
+ push(buildCompileTimeError( |
+ "Cyclic definition of factory '${name}'.", |
+ token.charOffset)); |
+ return; |
+ } |
+ } |
+ if (target is Constructor || |
+ (target is Procedure && target.kind == ProcedureKind.Factory)) { |
+ push(buildStaticInvocation( |
+ target, arguments, isConst: optional("const", token))); |
+ return; |
+ } else { |
+ errorName = debugName(type.cls.name, name); |
+ } |
+ } else { |
+ errorName = debugName(getNodeName(type), name); |
+ } |
+ errorName ??= name; |
+ push(throwNoSuchMethodError(errorName, arguments, token.charOffset)); |
+ } |
+ |
+ void handleConstExpression(Token token) { |
+ debugEvent("ConstExpression"); |
+ handleNewExpression(token); |
+ } |
+ |
+ void endTypeArguments(int count, Token beginToken, Token endToken) { |
+ debugEvent("TypeArguments"); |
+ push(popList(count)); |
+ } |
+ |
+ void handleThisExpression(Token token) { |
+ debugEvent("ThisExpression"); |
+ if (isFirstIdentifier && isInstanceContext) { |
+ push(new ThisAccessor(this, token.charOffset, inInitializer)); |
+ } else { |
+ push(new IncompleteError( |
+ this, token.charOffset, "Expected identifier, but got 'this'.")); |
+ } |
+ } |
+ |
+ void handleSuperExpression(Token token) { |
+ debugEvent("SuperExpression"); |
+ if (isFirstIdentifier && isInstanceContext) { |
+ Member member = this.member.target; |
+ member.transformerFlags |= TransformerFlag.superCalls; |
+ push(new ThisAccessor(this, token.charOffset, inInitializer, |
+ isSuper: true)); |
+ } else { |
+ push(new IncompleteError( |
+ this, token.charOffset, "Expected identifier, but got 'super'.")); |
+ } |
+ } |
+ |
+ void handleNamedArgument(Token colon) { |
+ debugEvent("NamedArgument"); |
+ Expression value = popForValue(); |
+ Identifier identifier = pop(); |
+ push(new NamedExpression(identifier.name, value)); |
+ } |
+ |
+ void endFunctionName(Token token) { |
+ debugEvent("FunctionName"); |
+ Identifier name = pop(); |
+ VariableDeclaration variable = new VariableDeclaration( |
+ name.name, isFinal: true); |
+ push(new FunctionDeclaration(variable, |
+ new FunctionNode(new InvalidStatement()))); |
+ scope[variable.name] = new KernelVariableBuilder(variable); |
+ enterLocalScope(); |
+ } |
+ |
+ void beginFunction(Token token) { |
+ debugEvent("beginFunction"); |
+ functionNestingLevel++; |
+ } |
+ |
+ void beginUnnamedFunction(Token token) { |
+ debugEvent("beginUnnamedFunction"); |
+ functionNestingLevel++; |
+ } |
+ |
+ void endFunction(Token getOrSet, Token endToken) { |
+ debugEvent("Function"); |
+ Statement body = popStatement(); |
+ AsyncMarker asyncModifier = pop(); |
+ if (functionNestingLevel != 0) { |
+ exitLocalScope(); |
+ } |
+ FormalParameters formals = pop(); |
+ List typeParameters = pop(); |
+ push(formals.addToFunction(new FunctionNode(body, |
+ typeParameters: typeParameters, asyncMarker: asyncModifier))); |
+ functionNestingLevel--; |
+ } |
+ |
+ void endFunctionDeclaration(Token token) { |
+ debugEvent("FunctionDeclaration"); |
+ FunctionNode function = pop(); |
+ exitLocalScope(); |
+ FunctionDeclaration declaration = pop(); |
+ function.returnType = pop() ?? const DynamicType(); |
+ pop(); // Modifiers. |
+ declaration.function = function; |
+ function.parent = declaration; |
+ push(declaration); |
+ } |
+ |
+ void endUnnamedFunction(Token token) { |
+ debugEvent("UnnamedFunction"); |
+ Statement body = popStatement(); |
+ AsyncMarker asyncModifier = pop(); |
+ exitLocalScope(); |
+ FormalParameters formals = pop(); |
+ List typeParameters = pop(); |
+ FunctionNode function = formals.addToFunction(new FunctionNode(body, |
+ typeParameters: typeParameters, asyncMarker: asyncModifier)); |
+ push(new FunctionExpression(function)); |
+ functionNestingLevel--; |
+ } |
+ |
+ void endDoWhileStatement( |
+ Token doKeyword, Token whileKeyword, Token endToken) { |
+ debugEvent("DoWhileStatement"); |
+ Expression condition = popForValue(); |
+ Statement body = popStatement(); |
+ JumpTarget continueTarget = exitContinueTarget(); |
+ JumpTarget breakTarget = exitBreakTarget(); |
+ if (continueTarget.hasUsers) { |
+ body = new LabeledStatement(body); |
+ continueTarget.resolveContinues(body); |
+ } |
+ Statement result = new DoStatement(body, condition); |
+ if (breakTarget.hasUsers) { |
+ result = new LabeledStatement(result); |
+ breakTarget.resolveBreaks(result); |
+ } |
+ exitLoopOrSwitch(result); |
+ } |
+ |
+ void endForIn( |
+ Token awaitToken, Token forToken, Token inKeyword, Token endToken) { |
+ debugEvent("ForIn"); |
+ Statement body = popStatement(); |
+ Expression expression = popForValue(); |
+ var lvalue = pop(); |
+ exitLocalScope(); |
+ JumpTarget continueTarget = exitContinueTarget(); |
+ JumpTarget breakTarget = exitBreakTarget(); |
+ if (continueTarget.hasUsers) { |
+ body = new LabeledStatement(body); |
+ continueTarget.resolveContinues(body); |
+ } |
+ VariableDeclaration variable; |
+ if (lvalue is VariableDeclaration) { |
+ variable = lvalue; |
+ } else if (lvalue is BuilderAccessor) { |
+ /// We are in this case, where `lvalue` isn't a [VariableDeclaration]: |
+ /// |
+ /// for (lvalue in expression) body |
+ /// |
+ /// This is normalized to: |
+ /// |
+ /// for (final #t in expression) { |
+ /// element = #t; |
asgerf
2017/01/13 18:39:06
element -> lvalue
There is no `element` mentioned
ahe
2017/01/16 08:32:09
Done.
|
+ /// body; |
+ /// } |
+ variable = new VariableDeclaration.forValue(null); |
+ body = combineStatements( |
+ new ExpressionStatement( |
+ lvalue.buildAssignment( |
+ new VariableGet(variable), voidContext: true)), |
+ body); |
+ } else { |
+ return inputError("Expected lvalue, but got ${lvalue}", |
+ forToken.next.next.charOffset); |
asgerf
2017/01/13 18:39:06
Value return in a void method?
ahe
2017/01/16 08:32:09
Changed to throw.
|
+ } |
+ Statement result = new ForInStatement(variable, expression, body, |
+ isAsync: awaitToken != null); |
+ if (breakTarget.hasUsers) { |
+ result = new LabeledStatement(result); |
+ breakTarget.resolveBreaks(result); |
+ } |
+ exitLoopOrSwitch(result); |
+ } |
+ |
+ void handleLabel(Token token) { |
+ debugEvent("Label"); |
+ Identifier identifier = pop(); |
+ push(new Label(identifier.name)); |
+ } |
+ |
+ void beginLabeledStatement(Token token, int labelCount) { |
+ debugEvent("beginLabeledStatement"); |
+ List<Label> labels = popList(labelCount); |
+ enterLocalScope(); |
+ LabelTarget target = new LabelTarget(); |
+ for (Label label in labels) { |
+ scope[label.name] = target; |
+ } |
+ push(target); |
+ } |
+ |
+ void endLabeledStatement(int labelCount) { |
+ debugEvent("LabeledStatement"); |
+ Statement statement = popStatement(); |
+ LabelTarget target = pop(); |
+ exitLocalScope(); |
+ if (target.breakTarget.hasUsers) { |
+ if (statement is! LabeledStatement) { |
+ statement = new LabeledStatement(statement); |
+ } |
+ target.breakTarget.resolveBreaks(statement); |
+ } |
+ if (target.continueTarget.hasUsers) { |
+ if (statement is! LabeledStatement) { |
+ statement = new LabeledStatement(statement); |
+ } |
+ target.continueTarget.resolveContinues(statement); |
+ } |
+ push(statement); |
+ } |
+ |
+ void endRethrowStatement(Token throwToken, Token endToken) { |
+ debugEvent("RethrowStatement"); |
+ push(new ExpressionStatement(new Rethrow())); |
+ } |
+ |
+ void handleFinallyBlock(Token finallyKeyword) { |
+ debugEvent("FinallyBlock"); |
+ // Do nothing, handled by [endTryStatement]. |
+ } |
+ |
+ void endWhileStatement(Token whileKeyword, Token endToken) { |
+ debugEvent("WhileStatement"); |
+ Statement body = popStatement(); |
+ Expression condition = popForValue(); |
+ JumpTarget continueTarget = exitContinueTarget(); |
+ JumpTarget breakTarget = exitBreakTarget(); |
+ if (continueTarget.hasUsers) { |
+ body = new LabeledStatement(body); |
+ continueTarget.resolveContinues(body); |
+ } |
+ Statement result = new WhileStatement(condition, body); |
+ if (breakTarget.hasUsers) { |
+ result = new LabeledStatement(result); |
+ breakTarget.resolveBreaks(result); |
+ } |
+ exitLoopOrSwitch(result); |
+ } |
+ |
+ void handleEmptyStatement(Token token) { |
+ debugEvent("EmptyStatement"); |
+ push(new EmptyStatement()); |
+ } |
+ |
+ void handleAssertStatement( |
+ Token assertKeyword, Token commaToken, Token semicolonToken) { |
+ debugEvent("AssertStatement"); |
+ Expression message = popIfNotNull(commaToken); |
+ Expression condition = popForValue(); |
+ push(new AssertStatement(condition, message)); |
+ } |
+ |
+ void endYieldStatement(Token yieldToken, Token starToken, Token endToken) { |
+ debugEvent("YieldStatement"); |
+ push(new YieldStatement(popForValue(), isYieldStar: starToken != null)); |
+ } |
+ |
+ void beginSwitchBlock(Token token) { |
+ debugEvent("beginSwitchBlock"); |
+ enterLocalScope(); |
+ enterSwitchScope(); |
+ enterBreakTarget(); |
+ } |
+ |
+ void beginSwitchCase(int labelCount, int expressionCount, Token firstToken) { |
+ debugEvent("beginSwitchCase"); |
+ List labelsAndExpressions = popList(labelCount + expressionCount); |
+ List<Label> labels = <Label>[]; |
+ List<Expression> expressions = <Expression>[]; |
+ if (labelsAndExpressions != null) { |
+ for (var labelOrExpression in labelsAndExpressions) { |
+ if (labelOrExpression is Label) { |
+ labels.add(labelOrExpression); |
+ } else { |
+ expressions.add(toValue(labelOrExpression)); |
+ } |
+ } |
+ } |
+ assert(scope == switchScope); |
+ for (Label label in labels) { |
+ Builder existing = scope.local[label.name]; |
+ if (existing == null) { |
+ scope[label.name] = createGotoTarget(); |
+ } else { |
+ // TODO(ahe): Should validate this is a goto target and not duplicated. |
+ } |
+ } |
+ push(expressions); |
+ push(labels); |
+ enterLocalScope(); |
+ } |
+ |
+ void handleSwitchCase( |
+ int labelCount, |
+ int expressionCount, |
+ Token defaultKeyword, |
+ int statementCount, |
+ Token firstToken, |
+ Token endToken) { |
+ debugEvent("SwitchCase"); |
+ Block block = popBlock(statementCount); |
+ exitLocalScope(); |
+ List<Label> labels = pop(); |
+ List<Expression> expressions = pop(); |
+ push(new SwitchCase(expressions, block, isDefault: defaultKeyword != null)); |
+ push(labels); |
+ } |
+ |
+ void endSwitchStatement(Token switchKeyword, Token endToken) { |
+ debugEvent("SwitchStatement"); |
+ // Do nothing. Handled by [endSwitchBlock]. |
+ } |
+ |
+ void endSwitchBlock(int caseCount, Token beginToken, Token endToken) { |
+ debugEvent("SwitchBlock"); |
+ List<SwitchCase> cases = |
+ new List<SwitchCase>.filled(caseCount, null, growable: true); |
+ for (int i = caseCount - 1; i >= 0; i--) { |
+ List<Label> labels = pop(); |
+ SwitchCase current = cases[i] = pop(); |
+ for (Label label in labels) { |
+ JumpTarget target = switchScope.lookup(label.name); |
+ if (target != null) { |
+ target.resolveGotos(current); |
+ } |
+ } |
+ // TODO(ahe): Validate that there's only one default and it's last. |
+ } |
+ JumpTarget target = exitBreakTarget(); |
+ exitSwitchScope(); |
+ exitLocalScope(); |
+ Expression expression = popForValue(); |
+ Statement result = new SwitchStatement(expression, cases); |
+ if (target.hasUsers) { |
+ result = new LabeledStatement(result); |
+ target.resolveBreaks(result); |
+ } |
+ exitLoopOrSwitch(result); |
+ } |
+ |
+ void handleCaseMatch(Token caseKeyword, Token colon) { |
+ debugEvent("CaseMatch"); |
+ // Do nothing. Handled by [handleSwitchCase]. |
+ } |
+ |
+ void handleBreakStatement( |
+ bool hasTarget, Token breakKeyword, Token endToken) { |
+ debugEvent("BreakStatement"); |
+ var target = breakTarget; |
+ String name; |
+ if (hasTarget) { |
+ Identifier identifier = pop(); |
+ name = identifier.name; |
+ target = scope.lookup(identifier.name); |
+ } |
+ if (target == null && name == null) { |
+ push(compileTimeErrorInLoopOrSwitch = |
+ buildCompileTimeErrorStatement( |
+ "No target of break.", breakKeyword.charOffset)); |
+ } else if (target == null || target is! JumpTarget |
+ || !target.isBreakTarget) { |
+ push(compileTimeErrorInLoopOrSwitch = |
+ buildCompileTimeErrorStatement("Can't break to '$name'.", |
+ breakKeyword.next.charOffset)); |
+ } else { |
+ BreakStatement statement = new BreakStatement(null); |
+ target.addBreak(statement); |
+ push(statement); |
+ } |
+ } |
+ |
+ void handleContinueStatement( |
+ bool hasTarget, Token continueKeyword, Token endToken) { |
+ debugEvent("ContinueStatement"); |
+ var target = continueTarget; |
+ String name; |
+ if (hasTarget) { |
+ Identifier identifier = pop(); |
+ name = identifier.name; |
+ target = scope.lookup(identifier.name); |
+ if (target != null && target is! JumpTarget) { |
+ push(compileTimeErrorInLoopOrSwitch = |
+ buildCompileTimeErrorStatement( |
+ "Target of continue must be a label.", |
+ continueKeyword.charOffset)); |
+ return; |
+ } |
+ if (target == null) { |
+ if (switchScope == null) { |
+ push(buildCompileTimeErrorStatement("Can't find label '$name'.", |
+ continueKeyword.next.charOffset)); |
+ return; |
+ } |
+ switchScope[identifier.name] = target = createGotoTarget(); |
+ } |
+ if (target.isGotoTarget) { |
+ ContinueSwitchStatement statement = new ContinueSwitchStatement(null); |
+ target.addGoto(statement); |
+ push(statement); |
+ return; |
+ } |
+ } |
+ if (target == null) { |
+ push(compileTimeErrorInLoopOrSwitch = |
+ buildCompileTimeErrorStatement("No target of continue.", |
+ continueKeyword.charOffset)); |
+ } else if (!target.isContinueTarget) { |
+ push(compileTimeErrorInLoopOrSwitch = |
+ buildCompileTimeErrorStatement("Can't continue at '$name'.", |
+ continueKeyword.next.charOffset)); |
+ } else { |
+ BreakStatement statement = new BreakStatement(null); |
+ target.addContinue(statement); |
+ push(statement); |
+ } |
+ } |
+ |
+ void endTypeVariable(Token token, Token extendsOrSuper) { |
+ logEvent("TypeVariable"); |
+ // TODO(ahe): Implement this when enabling generic method syntax. |
+ } |
+ |
+ void endTypeVariables(int count, Token beginToken, Token endToken) { |
+ logEvent("TypeVariables"); |
+ // TODO(ahe): Implement this when enabling generic method syntax. |
+ } |
+ |
+ void handleModifier(Token token) { |
+ debugEvent("Modifier"); |
+ // TODO(ahe): Copied from outline_builder.dart. |
+ push(new Modifier.fromString(token.stringValue)); |
+ } |
+ |
+ void handleModifiers(int count) { |
+ debugEvent("Modifiers"); |
+ // TODO(ahe): Copied from outline_builder.dart. |
+ push(popList(count) ?? NullValue.Modifiers); |
+ } |
+ |
+ void reportErrorHelper(Token token, ErrorKind kind, Map arguments) { |
+ super.reportErrorHelper(token, kind, arguments); |
+ if (!hasParserError) { |
+ print("$uri:${recoverableErrors.last}"); |
+ } |
+ hasParserError = true; |
+ } |
+ |
+ Token expectedExpression(Token token) { |
+ if (token is ErrorToken) { |
+ reportErrorToken(token); |
+ push(new Throw(new StringLiteral("${recoverableErrors.last}"))); |
+ do { |
+ token = token.next; |
+ } while (token is ErrorToken); |
+ return token; |
+ } else { |
+ push(new InvalidExpression()); |
+ return super.expectedExpression(token); |
+ } |
+ } |
+ |
+ Token expected(String string, Token token) { |
+ if (token is ErrorToken) { |
+ reportErrorToken(token); |
+ do { |
+ token = token.next; |
+ } while (token is ErrorToken); |
+ return token; |
+ } |
+ const List<String> trailing = const <String>[")", "}", ";", ","]; |
+ if (trailing.contains(token.stringValue) && trailing.contains(string)) { |
+ // We're just trying to get out an error. |
+ if (recoverableErrors.isNotEmpty) { |
+ reportError(token, ErrorKind.UNSPECIFIED, |
+ {"text": "Expected: '$string', but got '${token.value}'"}); |
+ } |
+ return token; |
+ } |
+ return super.expected(string, token); |
+ } |
+ |
+ void warning(error, [int charOffset = -1]) { |
+ String message = new InputError(uri, charOffset, error).format(); |
+ print(message); |
+ } |
+ |
+ void nit(error, [int charOffset = -1]) { |
+ if (!showNits) return; |
+ String message = new InputError(uri, charOffset, error).format(); |
+ print(message); |
+ } |
+ |
+ Expression buildCompileTimeError(error, [int charOffset = -1]) { |
+ String message = new InputError(uri, charOffset, error).format(); |
+ print(message); |
+ return new Throw(new StringLiteral(message)); |
+ } |
+ |
+ Statement buildCompileTimeErrorStatement(error, [int charOffset = -1]) { |
+ return new ExpressionStatement(buildCompileTimeError(error, charOffset)); |
+ } |
+ |
+ Initializer buildCompileTimeErrorIntializer(error, [int charOffset = -1]) { |
+ return new LocalInitializer( |
+ new VariableDeclaration.forValue( |
+ buildCompileTimeError(error, charOffset))); |
+ } |
+ |
+ |
+ Expression buildProblemExpression(Builder builder, String name) { |
asgerf
2017/01/13 18:39:06
Where is this method used? It's not called in this
ahe
2017/01/16 08:32:09
It's used in builder_accessors.dart.
|
+ if (builder is AmbiguousBuilder) { |
+ return buildCompileTimeError("Duplicated named: '$name'."); |
+ } else if (builder is AccessErrorBuilder) { |
+ return buildCompileTimeError("Access error: '$name'."); |
+ } else { |
+ return internalError("Unhandled: ${builder.runtimeType}"); |
+ } |
+ } |
+ |
+ void handleOperator(Token token) { |
+ debugEvent("Operator"); |
+ push(new Operator(token.stringValue)..fileOffset = token.charOffset); |
+ } |
+ |
+ dynamic inputError(String message, [int charOffset = -1]) { |
+ return errors.inputError(uri, charOffset, message); |
+ } |
+ |
+ void debugEvent(String name) { |
+ // printEvent(name); |
+ } |
+} |
+ |
+class UnresolvedIdentifier extends InvalidExpression { |
+ final Name name; |
+ |
+ UnresolvedIdentifier(this.name); |
+ |
+ String toString() => "unresolved-identifier($name)"; |
+} |
+ |
+class Identifier extends InvalidExpression { |
asgerf
2017/01/13 18:39:07
Do these need to be expressions at all? Can't they
ahe
2017/01/16 08:32:09
That's a good point. It's a bigger change due to s
|
+ final String name; |
+ |
+ Identifier(this.name); |
+ |
+ Expression get initializer => null; |
+ |
+ String toString() => "identifier($name)"; |
+} |
+ |
+class Operator extends InvalidExpression { |
+ final String name; |
+ |
+ Operator(this.name); |
+ |
+ String toString() => "operator($name)"; |
+} |
+ |
+class InitializedIdentifier extends Identifier { |
+ final Expression initializer; |
+ |
+ InitializedIdentifier(String name, this.initializer) |
+ : super(name); |
+ |
+ String toString() => "initialized-identifier($name, $initializer)"; |
+} |
+ |
+class Label extends InvalidExpression { |
+ String name; |
+ |
+ Label(this.name); |
+ |
+ String toString() => "label($name)"; |
+} |
+ |
+class CascadeReceiver extends Let { |
+ Let nextCascade; |
+ |
+ CascadeReceiver(VariableDeclaration variable) |
+ : super(variable, |
+ makeLet(new VariableDeclaration.forValue(new InvalidExpression()), |
+ new VariableGet(variable))) { |
+ nextCascade = body; |
+ } |
+ |
+ void extend() { |
+ assert(nextCascade.variable.initializer is! InvalidExpression); |
+ Let newCascade = makeLet( |
+ new VariableDeclaration.forValue(new InvalidExpression()), |
+ nextCascade.body); |
+ nextCascade.body = newCascade; |
+ newCascade.parent = nextCascade; |
+ nextCascade = newCascade; |
+ } |
+ |
+ void finalize(Expression expression) { |
+ assert(nextCascade.variable.initializer is InvalidExpression); |
+ nextCascade.variable.initializer = expression; |
+ expression.parent = nextCascade.variable; |
+ } |
+} |
+ |
+abstract class ContextAccessor extends BuilderAccessor { |
+ final BuilderHelper helper; |
+ |
+ final int charOffset; |
+ |
+ final BuilderAccessor accessor; |
+ |
+ ContextAccessor(this.helper, this.charOffset, this.accessor); |
+ |
+ String get plainNameForRead => internalError("Unsupported operation."); |
+ |
+ Expression doInvocation(int charOffset, Arguments arguments) { |
+ print("$uri:$charOffset: Internal error: Unhandled: ${runtimeType}"); |
+ return internalError("Unhandled: ${runtimeType}"); |
+ } |
+ |
+ Expression buildSimpleRead(); |
+ |
+ Expression buildForEffect(); |
+ |
+ Expression buildAssignment(Expression value, {bool voidContext: false}) { |
+ return internalError("not supported"); |
+ } |
+ |
+ Expression buildNullAwareAssignment(Expression value, DartType type, |
+ {bool voidContext: false}) { |
+ return internalError("not supported"); |
+ } |
+ |
+ Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
+ {bool voidContext: false, Procedure interfaceTarget}) { |
+ return internalError("not supported"); |
+ } |
+ |
+ Expression buildPrefixIncrement(Name binaryOperator, |
+ {bool voidContext: false, Procedure interfaceTarget}) { |
+ return internalError("not supported"); |
+ } |
+ |
+ Expression buildPostfixIncrement(Name binaryOperator, |
+ {bool voidContext: false, Procedure interfaceTarget}) { |
+ return internalError("not supported"); |
+ } |
+ |
+ makeInvalidRead() => internalError("not supported"); |
+ |
+ makeInvalidWrite(Expression value) => internalError("not supported"); |
+} |
+ |
+class DelayedAssignment extends ContextAccessor { |
+ final Expression value; |
+ |
+ final String assignmentOperator; |
+ |
+ DelayedAssignment(BuilderHelper helper, int charOffset, |
+ BuilderAccessor accessor, this.value, this.assignmentOperator) |
+ : super(helper, charOffset, accessor); |
+ |
+ Expression buildSimpleRead() { |
+ return handleAssignment(false); |
+ } |
+ |
+ Expression buildForEffect() { |
+ return handleAssignment(true); |
+ } |
+ |
+ Expression handleAssignment(bool voidContext) { |
+ if (identical("=", assignmentOperator)) { |
+ return accessor.buildAssignment(value, voidContext: voidContext); |
+ } else if (identical("+=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("+"), value, voidContext: voidContext); |
asgerf
2017/01/13 18:39:07
These Name objects can be shared (they are not AST
ahe
2017/01/16 08:32:09
Done.
|
+ } else if (identical("-=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("-"), value, voidContext: voidContext); |
+ } else if (identical("*=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("*"), value, voidContext: voidContext); |
+ } else if (identical("%=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("%"), value, voidContext: voidContext); |
+ } else if (identical("&=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("&"), value, voidContext: voidContext); |
+ } else if (identical("/=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("/"), value, voidContext: voidContext); |
+ } else if (identical("<<=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("<<"), value, voidContext: voidContext); |
+ } else if (identical(">>=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name(">>"), value, voidContext: voidContext); |
+ } else if (identical("??=", assignmentOperator)) { |
+ return accessor.buildNullAwareAssignment( |
+ value, const DynamicType(), voidContext: voidContext); |
+ } else if (identical("^=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("^"), value, voidContext: voidContext); |
+ } else if (identical("|=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("|"), value, voidContext: voidContext); |
+ } else if (identical("~/=", assignmentOperator)) { |
+ return accessor.buildCompoundAssignment( |
+ new Name("~/"), value, voidContext: voidContext); |
+ } else { |
+ return internalError("Unhandled: $assignmentOperator"); |
+ } |
+ } |
+ |
+ Initializer buildFieldInitializer( |
+ Map<String, FieldInitializer> initializers) { |
+ if (!identical("=", assignmentOperator) || |
+ !accessor.isThisPropertyAccessor) { |
+ return accessor.buildFieldInitializer(initializers); |
+ } |
+ String name = accessor.plainNameForRead; |
+ FieldInitializer initializer = initializers[name]; |
+ if (initializer != null && initializer.value == null) { |
+ initializers.remove(name); |
+ initializer.value = value |
+ ..parent = initializer; |
+ return initializer; |
+ } |
+ print("initializer = $initializer"); |
+ print("initializer.value = ${initializer?.value}"); |
asgerf
2017/01/13 18:39:06
These prints should be removed
ahe
2017/01/16 08:32:09
Done.
|
+ return accessor.buildFieldInitializer(initializers); |
+ } |
+} |
+ |
+class DelayedPostfixIncrement extends ContextAccessor { |
+ final Name binaryOperator; |
+ |
+ final Procedure interfaceTarget; |
+ |
+ DelayedPostfixIncrement(BuilderHelper helper, int charOffset, |
+ BuilderAccessor accessor, this.binaryOperator, this.interfaceTarget) |
+ : super(helper, charOffset, accessor); |
+ |
+ Expression buildSimpleRead() { |
+ return accessor.buildPostfixIncrement(binaryOperator, voidContext: false, |
+ interfaceTarget: interfaceTarget); |
+ } |
+ |
+ Expression buildForEffect() { |
+ return accessor.buildPostfixIncrement(binaryOperator, voidContext: true, |
+ interfaceTarget: interfaceTarget); |
+ } |
+} |
+ |
+class JumpTarget extends Builder { |
+ final List<Statement> users = <Statement>[]; |
+ |
+ final JumpTargetKind kind; |
+ |
+ JumpTarget(this.kind); |
+ |
+ bool get isBreakTarget => kind == JumpTargetKind.Break; |
+ |
+ bool get isContinueTarget => kind == JumpTargetKind.Continue; |
+ |
+ bool get isGotoTarget => kind == JumpTargetKind.Goto; |
+ |
+ bool get hasUsers => users.isNotEmpty; |
+ |
+ void addBreak(BreakStatement statement) { |
+ assert(isBreakTarget); |
+ users.add(statement); |
+ } |
+ |
+ void addContinue(BreakStatement statement) { |
+ assert(isContinueTarget); |
+ users.add(statement); |
+ } |
+ |
+ void addGoto(ContinueSwitchStatement statement) { |
+ assert(isGotoTarget); |
+ users.add(statement); |
+ } |
+ |
+ void resolveBreaks(LabeledStatement target) { |
+ assert(isBreakTarget); |
+ for (BreakStatement user in users) { |
+ user.target = target; |
+ } |
+ users.clear(); |
+ } |
+ |
+ void resolveContinues(LabeledStatement target) { |
+ assert(isContinueTarget); |
+ for (BreakStatement user in users) { |
+ user.target = target; |
+ } |
+ users.clear(); |
+ } |
+ |
+ void resolveGotos(SwitchCase target) { |
+ assert(isGotoTarget); |
+ for (ContinueSwitchStatement user in users) { |
+ user.target = target; |
+ } |
+ users.clear(); |
+ } |
+} |
+ |
+class LabelTarget extends Builder implements JumpTarget { |
+ final JumpTarget breakTarget = new JumpTarget(JumpTargetKind.Break); |
+ |
+ final JumpTarget continueTarget = new JumpTarget(JumpTargetKind.Continue); |
+ |
+ LabelTarget(); |
+ |
+ bool get hasUsers => breakTarget.hasUsers || continueTarget.hasUsers; |
+ |
+ List<Statement> get users => internalError("Unsupported operation."); |
+ |
+ JumpTargetKind get kind => internalError("Unsupported operation."); |
+ |
+ bool get isBreakTarget => true; |
+ |
+ bool get isContinueTarget => true; |
+ |
+ bool get isGotoTarget => false; |
+ |
+ void addBreak(BreakStatement statement) { |
+ breakTarget.addBreak(statement); |
+ } |
+ |
+ void addContinue(BreakStatement statement) { |
+ continueTarget.addContinue(statement); |
+ } |
+ |
+ void addGoto(ContinueSwitchStatement statement) { |
+ internalError("Unsupported operation."); |
+ } |
+ |
+ void resolveBreaks(LabeledStatement target) { |
+ breakTarget.resolveBreaks(target); |
+ } |
+ |
+ void resolveContinues(LabeledStatement target) { |
+ continueTarget.resolveContinues(target); |
+ } |
+ |
+ void resolveGotos(SwitchCase target) { |
+ internalError("Unsupported operation."); |
+ } |
+} |
+ |
+class OptionalFormals { |
+ final FormalParameterType kind; |
+ |
+ final List<VariableDeclaration> formals; |
+ |
+ OptionalFormals(this.kind, this.formals); |
+} |
+ |
+class FormalParameters { |
+ final List<VariableDeclaration> required; |
+ final OptionalFormals optional; |
+ |
+ FormalParameters(this.required, this.optional); |
+ |
+ FunctionNode addToFunction(FunctionNode function) { |
+ function.requiredParameterCount = required.length; |
+ function.positionalParameters.addAll(required); |
+ if (optional != null) { |
+ if (optional.kind.isPositional) { |
+ function.positionalParameters.addAll(optional.formals); |
+ } else { |
+ function.namedParameters.addAll(optional.formals); |
+ setParents(function.namedParameters, function); |
+ } |
+ } |
+ setParents(function.positionalParameters, function); |
+ return function; |
+ } |
+ |
+ FunctionType toFunctionType(DartType returnType) { |
+ returnType ??= const DynamicType(); |
+ int requiredParameterCount = required.length; |
+ List<DartType> positionalParameters = <DartType>[]; |
+ List<NamedType> namedParameters = const <NamedType>[]; |
+ for (VariableDeclaration parameter in required) { |
+ positionalParameters.add(parameter.type); |
+ } |
+ if (optional != null) { |
+ if (optional.kind.isPositional) { |
+ for (VariableDeclaration parameter in optional.formals) { |
+ positionalParameters.add(parameter.type); |
+ } |
+ } else { |
+ namedParameters = <NamedType>[]; |
+ for (VariableDeclaration parameter in optional.formals) { |
+ namedParameters.add(new NamedType(parameter.name, parameter.type)); |
+ } |
+ namedParameters.sort(); |
+ } |
+ } |
+ return new FunctionType(positionalParameters, returnType, |
+ namedParameters: namedParameters, |
+ requiredParameterCount: requiredParameterCount); |
+ } |
+ |
+ Scope computeFormalParameterScope(Scope parent) { |
+ if (required.length == 0 && optional == null) return parent; |
+ Map<String, Builder> local = <String, Builder>{}; |
+ for (VariableDeclaration parameter in required) { |
+ local[parameter.name] = new KernelVariableBuilder(parameter); |
+ } |
+ if (optional != null) { |
+ for (VariableDeclaration parameter in optional.formals) { |
+ local[parameter.name] = new KernelVariableBuilder(parameter); |
+ } |
+ } |
+ return new Scope(local, parent, isModifiable: false); |
+ } |
+} |
+ |
+/// Returns a block like this: |
+/// |
+/// { |
+/// statement; |
+/// body; |
+/// } |
+/// |
+/// If [body] is a [Block], it's returned with [statement] prepended to it. |
+Block combineStatements(Statement statement, Statement body) { |
+ if (body is Block) { |
+ body.statements.insert(0, statement); |
+ statement.parent = body; |
+ return body; |
+ } else { |
+ return new Block(<Statement>[statement, body]); |
+ } |
+} |
+ |
+String debugName(String className, String name, [String prefix]) { |
+ String result = name.isEmpty ? className : "$className.$name"; |
+ return prefix == null ? result : "$prefix.result"; |
+} |
+ |
+String getNodeName(Object node) { |
+ if (node is Identifier) { |
+ return node.name; |
+ } else if (node is UnresolvedIdentifier) { |
+ return node.name.name; |
+ } else if (node is TypeDeclarationBuilder) { |
+ return node.name; |
+ } else if (node is PrefixBuilder) { |
+ return node.name; |
+ } else if (node is ThisPropertyAccessor) { |
+ return node.name.name; |
+ } else { |
+ return internalError("Unhandled: ${node.runtimeType}"); |
+ } |
+} |