Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: pkg/analyzer/lib/src/dart/constant/evaluation.dart

Issue 1816923002: Move constant implementation out of generated (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library analyzer.src.dart.constant.evaluation;
6
7 import 'dart:collection';
8
9 import 'package:analyzer/context/declared_variables.dart';
10 import 'package:analyzer/dart/ast/ast.dart';
11 import 'package:analyzer/dart/ast/token.dart';
12 import 'package:analyzer/dart/ast/visitor.dart';
13 import 'package:analyzer/dart/constant/value.dart';
14 import 'package:analyzer/dart/element/element.dart';
15 import 'package:analyzer/dart/element/type.dart';
16 import 'package:analyzer/src/dart/constant/utilities.dart';
17 import 'package:analyzer/src/dart/constant/value.dart';
18 import 'package:analyzer/src/dart/element/element.dart';
19 import 'package:analyzer/src/dart/element/member.dart';
20 import 'package:analyzer/src/generated/engine.dart';
21 import 'package:analyzer/src/generated/engine.dart'
22 show AnalysisEngine, RecordingErrorListener;
23 import 'package:analyzer/src/generated/error.dart';
24 import 'package:analyzer/src/generated/java_core.dart';
25 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
26 import 'package:analyzer/src/generated/source.dart' show Source;
27 import 'package:analyzer/src/generated/type_system.dart'
28 show TypeSystem, TypeSystemImpl;
29 import 'package:analyzer/src/generated/utilities_collection.dart';
30 import 'package:analyzer/src/generated/utilities_dart.dart' show ParameterKind;
31 import 'package:analyzer/src/task/dart.dart';
32
33 /**
34 * Helper class encapsulating the methods for evaluating constants and
35 * constant instance creation expressions.
36 */
37 class ConstantEvaluationEngine {
38 /**
39 * Parameter to "fromEnvironment" methods that denotes the default value.
40 */
41 static String _DEFAULT_VALUE_PARAM = "defaultValue";
42
43 /**
44 * Source of RegExp matching any public identifier.
45 * From sdk/lib/internal/symbol.dart.
46 */
47 static String _PUBLIC_IDENTIFIER_RE =
48 "(?!${ConstantValueComputer._RESERVED_WORD_RE}\\b(?!\\\$))[a-zA-Z\$][\\w\$ ]*";
49
50 /**
51 * RegExp that validates a non-empty non-private symbol.
52 * From sdk/lib/internal/symbol.dart.
53 */
54 static RegExp _PUBLIC_SYMBOL_PATTERN = new RegExp(
55 "^(?:${ConstantValueComputer._OPERATOR_RE}\$|$_PUBLIC_IDENTIFIER_RE(?:=?\$ |[.](?!\$)))+?\$");
56
57 /**
58 * The type provider used to access the known types.
59 */
60 final TypeProvider typeProvider;
61
62 /**
63 * The type system. This is used to guess the types of constants when their
64 * exact value is unknown.
65 */
66 final TypeSystem typeSystem;
67
68 /**
69 * The set of variables declared on the command line using '-D'.
70 */
71 final DeclaredVariables _declaredVariables;
72
73 /**
74 * Validator used to verify correct dependency analysis when running unit
75 * tests.
76 */
77 final ConstantEvaluationValidator validator;
78
79 /**
80 * Initialize a newly created [ConstantEvaluationEngine]. The [typeProvider]
81 * is used to access known types. [_declaredVariables] is the set of
82 * variables declared on the command line using '-D'. The [validator], if
83 * given, is used to verify correct dependency analysis when running unit
84 * tests.
85 */
86 ConstantEvaluationEngine(this.typeProvider, this._declaredVariables,
87 {ConstantEvaluationValidator validator, TypeSystem typeSystem})
88 : validator = validator != null
89 ? validator
90 : new ConstantEvaluationValidator_ForProduction(),
91 typeSystem = typeSystem != null ? typeSystem : new TypeSystemImpl();
92
93 /**
94 * Check that the arguments to a call to fromEnvironment() are correct. The
95 * [arguments] are the AST nodes of the arguments. The [argumentValues] are
96 * the values of the unnamed arguments. The [namedArgumentValues] are the
97 * values of the named arguments. The [expectedDefaultValueType] is the
98 * allowed type of the "defaultValue" parameter (if present). Note:
99 * "defaultValue" is always allowed to be null. Return `true` if the arguments
100 * are correct, `false` if there is an error.
101 */
102 bool checkFromEnvironmentArguments(
103 NodeList<Expression> arguments,
104 List<DartObjectImpl> argumentValues,
105 HashMap<String, DartObjectImpl> namedArgumentValues,
106 InterfaceType expectedDefaultValueType) {
107 int argumentCount = arguments.length;
108 if (argumentCount < 1 || argumentCount > 2) {
109 return false;
110 }
111 if (arguments[0] is NamedExpression) {
112 return false;
113 }
114 if (!identical(argumentValues[0].type, typeProvider.stringType)) {
115 return false;
116 }
117 if (argumentCount == 2) {
118 if (arguments[1] is! NamedExpression) {
119 return false;
120 }
121 if (!((arguments[1] as NamedExpression).name.label.name ==
122 _DEFAULT_VALUE_PARAM)) {
123 return false;
124 }
125 ParameterizedType defaultValueType =
126 namedArgumentValues[_DEFAULT_VALUE_PARAM].type;
127 if (!(identical(defaultValueType, expectedDefaultValueType) ||
128 identical(defaultValueType, typeProvider.nullType))) {
129 return false;
130 }
131 }
132 return true;
133 }
134
135 /**
136 * Check that the arguments to a call to Symbol() are correct. The [arguments]
137 * are the AST nodes of the arguments. The [argumentValues] are the values of
138 * the unnamed arguments. The [namedArgumentValues] are the values of the
139 * named arguments. Return `true` if the arguments are correct, `false` if
140 * there is an error.
141 */
142 bool checkSymbolArguments(
143 NodeList<Expression> arguments,
144 List<DartObjectImpl> argumentValues,
145 HashMap<String, DartObjectImpl> namedArgumentValues) {
146 if (arguments.length != 1) {
147 return false;
148 }
149 if (arguments[0] is NamedExpression) {
150 return false;
151 }
152 if (!identical(argumentValues[0].type, typeProvider.stringType)) {
153 return false;
154 }
155 String name = argumentValues[0].toStringValue();
156 return isValidPublicSymbol(name);
157 }
158
159 /**
160 * Compute the constant value associated with the given [constant].
161 */
162 void computeConstantValue(ConstantEvaluationTarget constant) {
163 validator.beforeComputeValue(constant);
164 if (constant is ParameterElementImpl) {
165 Expression defaultValue = constant.constantInitializer;
166 if (defaultValue != null) {
167 RecordingErrorListener errorListener = new RecordingErrorListener();
168 ErrorReporter errorReporter =
169 new ErrorReporter(errorListener, constant.source);
170 DartObjectImpl dartObject =
171 defaultValue.accept(new ConstantVisitor(this, errorReporter));
172 constant.evaluationResult =
173 new EvaluationResultImpl(dartObject, errorListener.errors);
174 }
175 } else if (constant is VariableElementImpl) {
176 Expression constantInitializer = constant.constantInitializer;
177 if (constantInitializer != null) {
178 RecordingErrorListener errorListener = new RecordingErrorListener();
179 ErrorReporter errorReporter =
180 new ErrorReporter(errorListener, constant.source);
181 DartObjectImpl dartObject = constantInitializer
182 .accept(new ConstantVisitor(this, errorReporter));
183 // Only check the type for truly const declarations (don't check final
184 // fields with initializers, since their types may be generic. The type
185 // of the final field will be checked later, when the constructor is
186 // invoked).
187 if (dartObject != null && constant.isConst) {
188 if (!runtimeTypeMatch(dartObject, constant.type)) {
189 errorReporter.reportErrorForElement(
190 CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH,
191 constant,
192 [dartObject.type, constant.type]);
193 }
194 }
195 constant.evaluationResult =
196 new EvaluationResultImpl(dartObject, errorListener.errors);
197 }
198 } else if (constant is ConstructorElement) {
199 if (constant.isConst) {
200 // No evaluation needs to be done; constructor declarations are only in
201 // the dependency graph to ensure that any constants referred to in
202 // initializer lists and parameter defaults are evaluated before
203 // invocations of the constructor. However we do need to annotate the
204 // element as being free of constant evaluation cycles so that later
205 // code will know that it is safe to evaluate.
206 (constant as ConstructorElementImpl).isCycleFree = true;
207 }
208 } else if (constant is ElementAnnotationImpl) {
209 Annotation constNode = constant.annotationAst;
210 Element element = constant.element;
211 if (element is PropertyAccessorElement &&
212 element.variable is VariableElementImpl) {
213 // The annotation is a reference to a compile-time constant variable.
214 // Just copy the evaluation result.
215 VariableElementImpl variableElement =
216 element.variable as VariableElementImpl;
217 if (variableElement.evaluationResult != null) {
218 constant.evaluationResult = variableElement.evaluationResult;
219 } else {
220 // This could happen in the event that the annotation refers to a
221 // non-constant. The error is detected elsewhere, so just silently
222 // ignore it here.
223 constant.evaluationResult = new EvaluationResultImpl(null);
224 }
225 } else if (element is ConstructorElementImpl &&
226 element.isConst &&
227 constNode.arguments != null) {
228 RecordingErrorListener errorListener = new RecordingErrorListener();
229 ErrorReporter errorReporter =
230 new ErrorReporter(errorListener, constant.source);
231 ConstantVisitor constantVisitor =
232 new ConstantVisitor(this, errorReporter);
233 DartObjectImpl result = evaluateConstructorCall(
234 constNode,
235 constNode.arguments.arguments,
236 element,
237 constantVisitor,
238 errorReporter);
239 constant.evaluationResult =
240 new EvaluationResultImpl(result, errorListener.errors);
241 } else {
242 // This may happen for invalid code (e.g. failing to pass arguments
243 // to an annotation which references a const constructor). The error
244 // is detected elsewhere, so just silently ignore it here.
245 constant.evaluationResult = new EvaluationResultImpl(null);
246 }
247 } else if (constant is VariableElement) {
248 // constant is a VariableElement but not a VariableElementImpl. This can
249 // happen sometimes in the case of invalid user code (for example, a
250 // constant expression that refers to a non-static field inside a generic
251 // class will wind up referring to a FieldMember). The error is detected
252 // elsewhere, so just silently ignore it here.
253 } else {
254 // Should not happen.
255 assert(false);
256 AnalysisEngine.instance.logger.logError(
257 "Constant value computer trying to compute the value of a node of type ${constant.runtimeType}");
258 return;
259 }
260 }
261
262 /**
263 * Determine which constant elements need to have their values computed
264 * prior to computing the value of [constant], and report them using
265 * [callback].
266 */
267 void computeDependencies(
268 ConstantEvaluationTarget constant, ReferenceFinderCallback callback) {
269 ReferenceFinder referenceFinder = new ReferenceFinder(callback);
270 if (constant is ConstructorElement) {
271 constant = getConstructorImpl(constant);
272 }
273 if (constant is VariableElementImpl) {
274 Expression initializer = constant.constantInitializer;
275 if (initializer != null) {
276 initializer.accept(referenceFinder);
277 }
278 } else if (constant is ConstructorElementImpl) {
279 if (constant.isConst) {
280 constant.isCycleFree = false;
281 ConstructorElement redirectedConstructor =
282 getConstRedirectedConstructor(constant);
283 if (redirectedConstructor != null) {
284 ConstructorElement redirectedConstructorBase =
285 getConstructorImpl(redirectedConstructor);
286 callback(redirectedConstructorBase);
287 return;
288 } else if (constant.isFactory) {
289 // Factory constructor, but getConstRedirectedConstructor returned
290 // null. This can happen if we're visiting one of the special externa l
291 // const factory constructors in the SDK, or if the code contains
292 // errors (such as delegating to a non-const constructor, or delegatin g
293 // to a constructor that can't be resolved). In any of these cases,
294 // we'll evaluate calls to this constructor without having to refer to
295 // any other constants. So we don't need to report any dependencies.
296 return;
297 }
298 bool superInvocationFound = false;
299 List<ConstructorInitializer> initializers =
300 constant.constantInitializers;
301 for (ConstructorInitializer initializer in initializers) {
302 if (initializer is SuperConstructorInvocation) {
303 superInvocationFound = true;
304 }
305 initializer.accept(referenceFinder);
306 }
307 if (!superInvocationFound) {
308 // No explicit superconstructor invocation found, so we need to
309 // manually insert a reference to the implicit superconstructor.
310 InterfaceType superclass =
311 (constant.returnType as InterfaceType).superclass;
312 if (superclass != null && !superclass.isObject) {
313 ConstructorElement unnamedConstructor =
314 getConstructorImpl(superclass.element.unnamedConstructor);
315 if (unnamedConstructor != null) {
316 callback(unnamedConstructor);
317 }
318 }
319 }
320 for (FieldElement field in constant.enclosingElement.fields) {
321 // Note: non-static const isn't allowed but we handle it anyway so
322 // that we won't be confused by incorrect code.
323 if ((field.isFinal || field.isConst) &&
324 !field.isStatic &&
325 field.initializer != null) {
326 callback(field);
327 }
328 }
329 for (ParameterElement parameterElement in constant.parameters) {
330 callback(parameterElement);
331 }
332 }
333 } else if (constant is ElementAnnotationImpl) {
334 Annotation constNode = constant.annotationAst;
335 Element element = constant.element;
336 if (element is PropertyAccessorElement &&
337 element.variable is VariableElementImpl) {
338 // The annotation is a reference to a compile-time constant variable,
339 // so it depends on the variable.
340 callback(element.variable);
341 } else if (element is ConstructorElementImpl) {
342 // The annotation is a constructor invocation, so it depends on the
343 // constructor.
344 callback(element);
345 } else {
346 // This could happen in the event of invalid code. The error will be
347 // reported at constant evaluation time.
348 }
349 if (constNode.arguments != null) {
350 constNode.arguments.accept(referenceFinder);
351 }
352 } else if (constant is VariableElement) {
353 // constant is a VariableElement but not a VariableElementImpl. This can
354 // happen sometimes in the case of invalid user code (for example, a
355 // constant expression that refers to a non-static field inside a generic
356 // class will wind up referring to a FieldMember). So just don't bother
357 // computing any dependencies.
358 } else {
359 // Should not happen.
360 assert(false);
361 AnalysisEngine.instance.logger.logError(
362 "Constant value computer trying to compute the value of a node of type ${constant.runtimeType}");
363 }
364 }
365
366 /**
367 * Evaluate a call to fromEnvironment() on the bool, int, or String class. The
368 * [environmentValue] is the value fetched from the environment. The
369 * [builtInDefaultValue] is the value that should be used as the default if no
370 * "defaultValue" argument appears in [namedArgumentValues]. The
371 * [namedArgumentValues] are the values of the named parameters passed to
372 * fromEnvironment(). Return a [DartObjectImpl] object corresponding to the
373 * evaluated result.
374 */
375 DartObjectImpl computeValueFromEnvironment(
376 DartObject environmentValue,
377 DartObjectImpl builtInDefaultValue,
378 HashMap<String, DartObjectImpl> namedArgumentValues) {
379 DartObjectImpl value = environmentValue as DartObjectImpl;
380 if (value.isUnknown || value.isNull) {
381 // The name either doesn't exist in the environment or we couldn't parse
382 // the corresponding value.
383 // If the code supplied an explicit default, use it.
384 if (namedArgumentValues.containsKey(_DEFAULT_VALUE_PARAM)) {
385 value = namedArgumentValues[_DEFAULT_VALUE_PARAM];
386 } else if (value.isNull) {
387 // The code didn't supply an explicit default.
388 // The name exists in the environment but we couldn't parse the
389 // corresponding value.
390 // So use the built-in default value, because this is what the VM does.
391 value = builtInDefaultValue;
392 } else {
393 // The code didn't supply an explicit default.
394 // The name doesn't exist in the environment.
395 // The VM would use the built-in default value, but we don't want to do
396 // that for analysis because it's likely to lead to cascading errors.
397 // So just leave [value] in the unknown state.
398 }
399 }
400 return value;
401 }
402
403 DartObjectImpl evaluateConstructorCall(
404 AstNode node,
405 NodeList<Expression> arguments,
406 ConstructorElement constructor,
407 ConstantVisitor constantVisitor,
408 ErrorReporter errorReporter) {
409 if (!getConstructorImpl(constructor).isCycleFree) {
410 // It's not safe to evaluate this constructor, so bail out.
411 // TODO(paulberry): ensure that a reasonable error message is produced
412 // in this case, as well as other cases involving constant expression
413 // circularities (e.g. "compile-time constant expression depends on
414 // itself")
415 return new DartObjectImpl.validWithUnknownValue(constructor.returnType);
416 }
417 int argumentCount = arguments.length;
418 List<DartObjectImpl> argumentValues =
419 new List<DartObjectImpl>(argumentCount);
420 List<Expression> argumentNodes = new List<Expression>(argumentCount);
421 HashMap<String, DartObjectImpl> namedArgumentValues =
422 new HashMap<String, DartObjectImpl>();
423 HashMap<String, NamedExpression> namedArgumentNodes =
424 new HashMap<String, NamedExpression>();
425 for (int i = 0; i < argumentCount; i++) {
426 Expression argument = arguments[i];
427 if (argument is NamedExpression) {
428 String name = argument.name.label.name;
429 namedArgumentValues[name] =
430 constantVisitor._valueOf(argument.expression);
431 namedArgumentNodes[name] = argument;
432 argumentValues[i] = typeProvider.nullObject;
433 } else {
434 argumentValues[i] = constantVisitor._valueOf(argument);
435 argumentNodes[i] = argument;
436 }
437 }
438 constructor = followConstantRedirectionChain(constructor);
439 InterfaceType definingClass = constructor.returnType as InterfaceType;
440 if (constructor.isFactory) {
441 // We couldn't find a non-factory constructor.
442 // See if it's because we reached an external const factory constructor
443 // that we can emulate.
444 if (constructor.name == "fromEnvironment") {
445 if (!checkFromEnvironmentArguments(
446 arguments, argumentValues, namedArgumentValues, definingClass)) {
447 errorReporter.reportErrorForNode(
448 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
449 return null;
450 }
451 String variableName =
452 argumentCount < 1 ? null : argumentValues[0].toStringValue();
453 if (identical(definingClass, typeProvider.boolType)) {
454 DartObject valueFromEnvironment;
455 valueFromEnvironment =
456 _declaredVariables.getBool(typeProvider, variableName);
457 return computeValueFromEnvironment(
458 valueFromEnvironment,
459 new DartObjectImpl(typeProvider.boolType, BoolState.FALSE_STATE),
460 namedArgumentValues);
461 } else if (identical(definingClass, typeProvider.intType)) {
462 DartObject valueFromEnvironment;
463 valueFromEnvironment =
464 _declaredVariables.getInt(typeProvider, variableName);
465 return computeValueFromEnvironment(
466 valueFromEnvironment,
467 new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE),
468 namedArgumentValues);
469 } else if (identical(definingClass, typeProvider.stringType)) {
470 DartObject valueFromEnvironment;
471 valueFromEnvironment =
472 _declaredVariables.getString(typeProvider, variableName);
473 return computeValueFromEnvironment(
474 valueFromEnvironment,
475 new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE),
476 namedArgumentValues);
477 }
478 } else if (constructor.name == "" &&
479 identical(definingClass, typeProvider.symbolType) &&
480 argumentCount == 1) {
481 if (!checkSymbolArguments(
482 arguments, argumentValues, namedArgumentValues)) {
483 errorReporter.reportErrorForNode(
484 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
485 return null;
486 }
487 String argumentValue = argumentValues[0].toStringValue();
488 return new DartObjectImpl(
489 definingClass, new SymbolState(argumentValue));
490 }
491 // Either it's an external const factory constructor that we can't
492 // emulate, or an error occurred (a cycle, or a const constructor trying
493 // to delegate to a non-const constructor).
494 // In the former case, the best we can do is consider it an unknown value.
495 // In the latter case, the error has already been reported, so considering
496 // it an unknown value will suppress further errors.
497 return new DartObjectImpl.validWithUnknownValue(definingClass);
498 }
499 ConstructorElementImpl constructorBase = getConstructorImpl(constructor);
500 validator.beforeGetConstantInitializers(constructorBase);
501 List<ConstructorInitializer> initializers =
502 constructorBase.constantInitializers;
503 if (initializers == null) {
504 // This can happen in some cases where there are compile errors in the
505 // code being analyzed (for example if the code is trying to create a
506 // const instance using a non-const constructor, or the node we're
507 // visiting is involved in a cycle). The error has already been reported,
508 // so consider it an unknown value to suppress further errors.
509 return new DartObjectImpl.validWithUnknownValue(definingClass);
510 }
511 HashMap<String, DartObjectImpl> fieldMap =
512 new HashMap<String, DartObjectImpl>();
513 // Start with final fields that are initialized at their declaration site.
514 for (FieldElement field in constructor.enclosingElement.fields) {
515 if ((field.isFinal || field.isConst) &&
516 !field.isStatic &&
517 field is ConstFieldElementImpl) {
518 validator.beforeGetFieldEvaluationResult(field);
519 EvaluationResultImpl evaluationResult = field.evaluationResult;
520 // It is possible that the evaluation result is null.
521 // This happens for example when we have duplicate fields.
522 // class Test {final x = 1; final x = 2; const Test();}
523 if (evaluationResult == null) {
524 continue;
525 }
526 // Match the value and the type.
527 DartType fieldType =
528 FieldMember.from(field, constructor.returnType).type;
529 DartObjectImpl fieldValue = evaluationResult.value;
530 if (fieldValue != null && !runtimeTypeMatch(fieldValue, fieldType)) {
531 errorReporter.reportErrorForNode(
532 CheckedModeCompileTimeErrorCode
533 .CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH,
534 node,
535 [fieldValue.type, field.name, fieldType]);
536 }
537 fieldMap[field.name] = fieldValue;
538 }
539 }
540 // Now evaluate the constructor declaration.
541 HashMap<String, DartObjectImpl> parameterMap =
542 new HashMap<String, DartObjectImpl>();
543 List<ParameterElement> parameters = constructor.parameters;
544 int parameterCount = parameters.length;
545 for (int i = 0; i < parameterCount; i++) {
546 ParameterElement parameter = parameters[i];
547 ParameterElement baseParameter = parameter;
548 while (baseParameter is ParameterMember) {
549 baseParameter = (baseParameter as ParameterMember).baseElement;
550 }
551 DartObjectImpl argumentValue = null;
552 AstNode errorTarget = null;
553 if (baseParameter.parameterKind == ParameterKind.NAMED) {
554 argumentValue = namedArgumentValues[baseParameter.name];
555 errorTarget = namedArgumentNodes[baseParameter.name];
556 } else if (i < argumentCount) {
557 argumentValue = argumentValues[i];
558 errorTarget = argumentNodes[i];
559 }
560 if (errorTarget == null) {
561 // No argument node that we can direct error messages to, because we
562 // are handling an optional parameter that wasn't specified. So just
563 // direct error messages to the constructor call.
564 errorTarget = node;
565 }
566 if (argumentValue == null && baseParameter is ParameterElementImpl) {
567 // The parameter is an optional positional parameter for which no value
568 // was provided, so use the default value.
569 validator.beforeGetParameterDefault(baseParameter);
570 EvaluationResultImpl evaluationResult = baseParameter.evaluationResult;
571 if (evaluationResult == null) {
572 // No default was provided, so the default value is null.
573 argumentValue = typeProvider.nullObject;
574 } else if (evaluationResult.value != null) {
575 argumentValue = evaluationResult.value;
576 }
577 }
578 if (argumentValue != null) {
579 if (!runtimeTypeMatch(argumentValue, parameter.type)) {
580 errorReporter.reportErrorForNode(
581 CheckedModeCompileTimeErrorCode
582 .CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH,
583 errorTarget,
584 [argumentValue.type, parameter.type]);
585 }
586 if (baseParameter.isInitializingFormal) {
587 FieldElement field = (parameter as FieldFormalParameterElement).field;
588 if (field != null) {
589 DartType fieldType = field.type;
590 if (fieldType != parameter.type) {
591 // We've already checked that the argument can be assigned to the
592 // parameter; we also need to check that it can be assigned to
593 // the field.
594 if (!runtimeTypeMatch(argumentValue, fieldType)) {
595 errorReporter.reportErrorForNode(
596 CheckedModeCompileTimeErrorCode
597 .CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH,
598 errorTarget,
599 [argumentValue.type, fieldType]);
600 }
601 }
602 String fieldName = field.name;
603 if (fieldMap.containsKey(fieldName)) {
604 errorReporter.reportErrorForNode(
605 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
606 }
607 fieldMap[fieldName] = argumentValue;
608 }
609 } else {
610 String name = baseParameter.name;
611 parameterMap[name] = argumentValue;
612 }
613 }
614 }
615 ConstantVisitor initializerVisitor = new ConstantVisitor(
616 this, errorReporter,
617 lexicalEnvironment: parameterMap);
618 String superName = null;
619 NodeList<Expression> superArguments = null;
620 for (ConstructorInitializer initializer in initializers) {
621 if (initializer is ConstructorFieldInitializer) {
622 ConstructorFieldInitializer constructorFieldInitializer = initializer;
623 Expression initializerExpression =
624 constructorFieldInitializer.expression;
625 DartObjectImpl evaluationResult =
626 initializerExpression.accept(initializerVisitor);
627 if (evaluationResult != null) {
628 String fieldName = constructorFieldInitializer.fieldName.name;
629 if (fieldMap.containsKey(fieldName)) {
630 errorReporter.reportErrorForNode(
631 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node);
632 }
633 fieldMap[fieldName] = evaluationResult;
634 PropertyAccessorElement getter = definingClass.getGetter(fieldName);
635 if (getter != null) {
636 PropertyInducingElement field = getter.variable;
637 if (!runtimeTypeMatch(evaluationResult, field.type)) {
638 errorReporter.reportErrorForNode(
639 CheckedModeCompileTimeErrorCode
640 .CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH,
641 node,
642 [evaluationResult.type, fieldName, field.type]);
643 }
644 }
645 }
646 } else if (initializer is SuperConstructorInvocation) {
647 SuperConstructorInvocation superConstructorInvocation = initializer;
648 SimpleIdentifier name = superConstructorInvocation.constructorName;
649 if (name != null) {
650 superName = name.name;
651 }
652 superArguments = superConstructorInvocation.argumentList.arguments;
653 } else if (initializer is RedirectingConstructorInvocation) {
654 // This is a redirecting constructor, so just evaluate the constructor
655 // it redirects to.
656 ConstructorElement constructor = initializer.staticElement;
657 if (constructor != null && constructor.isConst) {
658 return evaluateConstructorCall(
659 node,
660 initializer.argumentList.arguments,
661 constructor,
662 initializerVisitor,
663 errorReporter);
664 }
665 }
666 }
667 // Evaluate explicit or implicit call to super().
668 InterfaceType superclass = definingClass.superclass;
669 if (superclass != null && !superclass.isObject) {
670 ConstructorElement superConstructor =
671 superclass.lookUpConstructor(superName, constructor.library);
672 if (superConstructor != null) {
673 if (superArguments == null) {
674 superArguments = new NodeList<Expression>(null);
675 }
676 evaluateSuperConstructorCall(node, fieldMap, superConstructor,
677 superArguments, initializerVisitor, errorReporter);
678 }
679 }
680 return new DartObjectImpl(definingClass, new GenericState(fieldMap));
681 }
682
683 void evaluateSuperConstructorCall(
684 AstNode node,
685 HashMap<String, DartObjectImpl> fieldMap,
686 ConstructorElement superConstructor,
687 NodeList<Expression> superArguments,
688 ConstantVisitor initializerVisitor,
689 ErrorReporter errorReporter) {
690 if (superConstructor != null && superConstructor.isConst) {
691 DartObjectImpl evaluationResult = evaluateConstructorCall(node,
692 superArguments, superConstructor, initializerVisitor, errorReporter);
693 if (evaluationResult != null) {
694 fieldMap[GenericState.SUPERCLASS_FIELD] = evaluationResult;
695 }
696 }
697 }
698
699 /**
700 * Attempt to follow the chain of factory redirections until a constructor is
701 * reached which is not a const factory constructor. Return the constant
702 * constructor which terminates the chain of factory redirections, if the
703 * chain terminates. If there is a problem (e.g. a redirection can't be found,
704 * or a cycle is encountered), the chain will be followed as far as possible
705 * and then a const factory constructor will be returned.
706 */
707 ConstructorElement followConstantRedirectionChain(
708 ConstructorElement constructor) {
709 HashSet<ConstructorElement> constructorsVisited =
710 new HashSet<ConstructorElement>();
711 while (true) {
712 ConstructorElement redirectedConstructor =
713 getConstRedirectedConstructor(constructor);
714 if (redirectedConstructor == null) {
715 break;
716 } else {
717 ConstructorElement constructorBase = getConstructorImpl(constructor);
718 constructorsVisited.add(constructorBase);
719 ConstructorElement redirectedConstructorBase =
720 getConstructorImpl(redirectedConstructor);
721 if (constructorsVisited.contains(redirectedConstructorBase)) {
722 // Cycle in redirecting factory constructors--this is not allowed
723 // and is checked elsewhere--see
724 // [ErrorVerifier.checkForRecursiveFactoryRedirect()]).
725 break;
726 }
727 }
728 constructor = redirectedConstructor;
729 }
730 return constructor;
731 }
732
733 /**
734 * Generate an error indicating that the given [constant] is not a valid
735 * compile-time constant because it references at least one of the constants
736 * in the given [cycle], each of which directly or indirectly references the
737 * constant.
738 */
739 void generateCycleError(Iterable<ConstantEvaluationTarget> cycle,
740 ConstantEvaluationTarget constant) {
741 if (constant is VariableElement) {
742 RecordingErrorListener errorListener = new RecordingErrorListener();
743 ErrorReporter errorReporter =
744 new ErrorReporter(errorListener, constant.source);
745 // TODO(paulberry): It would be really nice if we could extract enough
746 // information from the 'cycle' argument to provide the user with a
747 // description of the cycle.
748 errorReporter.reportErrorForElement(
749 CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, constant, []);
750 (constant as VariableElementImpl).evaluationResult =
751 new EvaluationResultImpl(null, errorListener.errors);
752 } else if (constant is ConstructorElement) {
753 // We don't report cycle errors on constructor declarations since there
754 // is nowhere to put the error information.
755 } else {
756 // Should not happen. Formal parameter defaults and annotations should
757 // never appear as part of a cycle because they can't be referred to.
758 assert(false);
759 AnalysisEngine.instance.logger.logError(
760 "Constant value computer trying to report a cycle error for a node of type ${constant.runtimeType}");
761 }
762 }
763
764 /**
765 * If [constructor] redirects to another const constructor, return the
766 * const constructor it redirects to. Otherwise return `null`.
767 */
768 ConstructorElement getConstRedirectedConstructor(
769 ConstructorElement constructor) {
770 if (!constructor.isFactory) {
771 return null;
772 }
773 if (identical(constructor.enclosingElement.type, typeProvider.symbolType)) {
774 // The dart:core.Symbol has a const factory constructor that redirects
775 // to dart:_internal.Symbol. That in turn redirects to an external
776 // const constructor, which we won't be able to evaluate.
777 // So stop following the chain of redirections at dart:core.Symbol, and
778 // let [evaluateInstanceCreationExpression] handle it specially.
779 return null;
780 }
781 ConstructorElement redirectedConstructor =
782 constructor.redirectedConstructor;
783 if (redirectedConstructor == null) {
784 // This can happen if constructor is an external factory constructor.
785 return null;
786 }
787 if (!redirectedConstructor.isConst) {
788 // Delegating to a non-const constructor--this is not allowed (and
789 // is checked elsewhere--see
790 // [ErrorVerifier.checkForRedirectToNonConstConstructor()]).
791 return null;
792 }
793 return redirectedConstructor;
794 }
795
796 /**
797 * Check if the object [obj] matches the type [type] according to runtime type
798 * checking rules.
799 */
800 bool runtimeTypeMatch(DartObjectImpl obj, DartType type) {
801 if (obj.isNull) {
802 return true;
803 }
804 if (type.isUndefined) {
805 return false;
806 }
807 return obj.type.isSubtypeOf(type);
808 }
809
810 /**
811 * Determine whether the given string is a valid name for a public symbol
812 * (i.e. whether it is allowed for a call to the Symbol constructor).
813 */
814 static bool isValidPublicSymbol(String name) =>
815 name.isEmpty ||
816 name == "void" ||
817 new JavaPatternMatcher(_PUBLIC_SYMBOL_PATTERN, name).matches();
818 }
819
820 /**
821 * Interface used by unit tests to verify correct dependency analysis during
822 * constant evaluation.
823 */
824 abstract class ConstantEvaluationValidator {
825 /**
826 * This method is called just before computing the constant value associated
827 * with [constant]. Unit tests will override this method to introduce
828 * additional error checking.
829 */
830 void beforeComputeValue(ConstantEvaluationTarget constant);
831
832 /**
833 * This method is called just before getting the constant initializers
834 * associated with the [constructor]. Unit tests will override this method to
835 * introduce additional error checking.
836 */
837 void beforeGetConstantInitializers(ConstructorElement constructor);
838
839 /**
840 * This method is called just before retrieving an evaluation result from an
841 * element. Unit tests will override it to introduce additional error
842 * checking.
843 */
844 void beforeGetEvaluationResult(ConstantEvaluationTarget constant);
845
846 /**
847 * This method is called just before getting the constant value of a field
848 * with an initializer. Unit tests will override this method to introduce
849 * additional error checking.
850 */
851 void beforeGetFieldEvaluationResult(FieldElementImpl field);
852
853 /**
854 * This method is called just before getting a parameter's default value. Unit
855 * tests will override this method to introduce additional error checking.
856 */
857 void beforeGetParameterDefault(ParameterElement parameter);
858 }
859
860 /**
861 * Implementation of [ConstantEvaluationValidator] used in production; does no
862 * validation.
863 */
864 class ConstantEvaluationValidator_ForProduction
865 implements ConstantEvaluationValidator {
866 @override
867 void beforeComputeValue(ConstantEvaluationTarget constant) {}
868
869 @override
870 void beforeGetConstantInitializers(ConstructorElement constructor) {}
871
872 @override
873 void beforeGetEvaluationResult(ConstantEvaluationTarget constant) {}
874
875 @override
876 void beforeGetFieldEvaluationResult(FieldElementImpl field) {}
877
878 @override
879 void beforeGetParameterDefault(ParameterElement parameter) {}
880 }
881
882 /**
883 * An object used to compute the values of constant variables and constant
884 * constructor invocations in one or more compilation units. The expected usage
885 * pattern is for the compilation units to be added to this computer using the
886 * method [add] and then for the method [computeValues] to be invoked exactly
887 * once. Any use of an instance after invoking the method [computeValues] will
888 * result in unpredictable behavior.
889 */
890 class ConstantValueComputer {
891 /**
892 * Source of RegExp matching declarable operator names.
893 * From sdk/lib/internal/symbol.dart.
894 */
895 static String _OPERATOR_RE =
896 "(?:[\\-+*/%&|^]|\\[\\]=?|==|~/?|<[<=]?|>[>=]?|unary-)";
897
898 /**
899 * Source of RegExp matching Dart reserved words.
900 * From sdk/lib/internal/symbol.dart.
901 */
902 static String _RESERVED_WORD_RE =
903 "(?:assert|break|c(?:a(?:se|tch)|lass|on(?:st|tinue))|d(?:efault|o)|e(?:ls e|num|xtends)|f(?:alse|inal(?:ly)?|or)|i[fns]|n(?:ew|ull)|ret(?:hrow|urn)|s(?:up er|witch)|t(?:h(?:is|row)|r(?:ue|y))|v(?:ar|oid)|w(?:hile|ith))";
904
905 /**
906 * A graph in which the nodes are the constants, and the edges are from each
907 * constant to the other constants that are referenced by it.
908 */
909 DirectedGraph<ConstantEvaluationTarget> referenceGraph =
910 new DirectedGraph<ConstantEvaluationTarget>();
911
912 /**
913 * The elements whose constant values need to be computed. Any elements
914 * which appear in [referenceGraph] but not in this set either belong to a
915 * different library cycle (and hence don't need to be recomputed) or were
916 * computed during a previous stage of resolution stage (e.g. constants
917 * associated with enums).
918 */
919 HashSet<ConstantEvaluationTarget> _constantsToCompute =
920 new HashSet<ConstantEvaluationTarget>();
921
922 /**
923 * The evaluation engine that does the work of evaluating instance creation
924 * expressions.
925 */
926 final ConstantEvaluationEngine evaluationEngine;
927
928 final AnalysisContext _context;
929
930 /**
931 * Initialize a newly created constant value computer. The [typeProvider] is
932 * the type provider used to access known types. The [declaredVariables] is
933 * the set of variables declared on the command line using '-D'.
934 */
935 ConstantValueComputer(this._context, TypeProvider typeProvider,
936 DeclaredVariables declaredVariables,
937 [ConstantEvaluationValidator validator, TypeSystem typeSystem])
938 : evaluationEngine = new ConstantEvaluationEngine(
939 typeProvider, declaredVariables,
940 validator: validator, typeSystem: typeSystem);
941
942 /**
943 * Add the constants in the given compilation [unit] to the list of constants
944 * whose value needs to be computed.
945 */
946 void add(CompilationUnit unit, Source source, Source librarySource) {
947 ConstantFinder constantFinder =
948 new ConstantFinder(_context, source, librarySource);
949 unit.accept(constantFinder);
950 _constantsToCompute.addAll(constantFinder.constantsToCompute);
951 }
952
953 /**
954 * Compute values for all of the constants in the compilation units that were
955 * added.
956 */
957 void computeValues() {
958 for (ConstantEvaluationTarget constant in _constantsToCompute) {
959 referenceGraph.addNode(constant);
960 evaluationEngine.computeDependencies(constant,
961 (ConstantEvaluationTarget dependency) {
962 referenceGraph.addEdge(constant, dependency);
963 });
964 }
965 List<List<ConstantEvaluationTarget>> topologicalSort =
966 referenceGraph.computeTopologicalSort();
967 for (List<ConstantEvaluationTarget> constantsInCycle in topologicalSort) {
968 if (constantsInCycle.length == 1) {
969 ConstantEvaluationTarget constant = constantsInCycle[0];
970 if (!referenceGraph.getTails(constant).contains(constant)) {
971 _computeValueFor(constant);
972 continue;
973 }
974 }
975 for (ConstantEvaluationTarget constant in constantsInCycle) {
976 evaluationEngine.generateCycleError(constantsInCycle, constant);
977 }
978 }
979 }
980
981 /**
982 * Compute a value for the given [constant].
983 */
984 void _computeValueFor(ConstantEvaluationTarget constant) {
985 if (!_constantsToCompute.contains(constant)) {
986 // Element is in the dependency graph but should have been computed by
987 // a previous stage of analysis.
988 // TODO(paulberry): once we have moved over to the new task model, this
989 // should only occur for constants associated with enum members. Once
990 // that happens we should add an assertion to verify that it doesn't
991 // occur in any other cases.
992 return;
993 }
994 evaluationEngine.computeConstantValue(constant);
995 }
996 }
997
998 /**
999 * A visitor used to evaluate constant expressions to produce their compile-time
1000 * value. According to the Dart Language Specification: <blockquote> A constant
1001 * expression is one of the following:
1002 *
1003 * * A literal number.
1004 * * A literal boolean.
1005 * * A literal string where any interpolated expression is a compile-time
1006 * constant that evaluates to a numeric, string or boolean value or to
1007 * <b>null</b>.
1008 * * A literal symbol.
1009 * * <b>null</b>.
1010 * * A qualified reference to a static constant variable.
1011 * * An identifier expression that denotes a constant variable, class or type
1012 * alias.
1013 * * A constant constructor invocation.
1014 * * A constant list literal.
1015 * * A constant map literal.
1016 * * A simple or qualified identifier denoting a top-level function or a static
1017 * method.
1018 * * A parenthesized expression <i>(e)</i> where <i>e</i> is a constant
1019 * expression.
1020 * * An expression of the form <i>identical(e<sub>1</sub>, e<sub>2</sub>)</i>
1021 * where <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
1022 * expressions and <i>identical()</i> is statically bound to the predefined
1023 * dart function <i>identical()</i> discussed above.
1024 * * An expression of one of the forms <i>e<sub>1</sub> == e<sub>2</sub></i> or
1025 * <i>e<sub>1</sub> != e<sub>2</sub></i> where <i>e<sub>1</sub></i> and
1026 * <i>e<sub>2</sub></i> are constant expressions that evaluate to a numeric,
1027 * string or boolean value.
1028 * * An expression of one of the forms <i>!e</i>, <i>e<sub>1</sub> &amp;&amp;
1029 * e<sub>2</sub></i> or <i>e<sub>1</sub> || e<sub>2</sub></i>, where <i>e</i>,
1030 * <i>e1</sub></i> and <i>e2</sub></i> are constant expressions that evaluate
1031 * to a boolean value.
1032 * * An expression of one of the forms <i>~e</i>, <i>e<sub>1</sub> ^
1033 * e<sub>2</sub></i>, <i>e<sub>1</sub> &amp; e<sub>2</sub></i>,
1034 * <i>e<sub>1</sub> | e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;&gt;
1035 * e<sub>2</sub></i> or <i>e<sub>1</sub> &lt;&lt; e<sub>2</sub></i>, where
1036 * <i>e</i>, <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant
1037 * expressions that evaluate to an integer value or to <b>null</b>.
1038 * * An expression of one of the forms <i>-e</i>, <i>e<sub>1</sub> +
1039 * e<sub>2</sub></i>, <i>e<sub>1</sub> - e<sub>2</sub></i>, <i>e<sub>1</sub> *
1040 * e<sub>2</sub></i>, <i>e<sub>1</sub> / e<sub>2</sub></i>, <i>e<sub>1</sub>
1041 * ~/ e<sub>2</sub></i>, <i>e<sub>1</sub> &gt; e<sub>2</sub></i>,
1042 * <i>e<sub>1</sub> &lt; e<sub>2</sub></i>, <i>e<sub>1</sub> &gt;=
1043 * e<sub>2</sub></i>, <i>e<sub>1</sub> &lt;= e<sub>2</sub></i> or
1044 * <i>e<sub>1</sub> % e<sub>2</sub></i>, where <i>e</i>, <i>e<sub>1</sub></i>
1045 * and <i>e<sub>2</sub></i> are constant expressions that evaluate to a
1046 * numeric value or to <b>null</b>.
1047 * * An expression of the form <i>e<sub>1</sub> ? e<sub>2</sub> :
1048 * e<sub>3</sub></i> where <i>e<sub>1</sub></i>, <i>e<sub>2</sub></i> and
1049 * <i>e<sub>3</sub></i> are constant expressions, and <i>e<sub>1</sub></i>
1050 * evaluates to a boolean value.
1051 * </blockquote>
1052 */
1053 class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
1054 /**
1055 * The type provider used to access the known types.
1056 */
1057 final ConstantEvaluationEngine evaluationEngine;
1058
1059 final HashMap<String, DartObjectImpl> _lexicalEnvironment;
1060
1061 /**
1062 * Error reporter that we use to report errors accumulated while computing the
1063 * constant.
1064 */
1065 final ErrorReporter _errorReporter;
1066
1067 /**
1068 * Helper class used to compute constant values.
1069 */
1070 DartObjectComputer _dartObjectComputer;
1071
1072 /**
1073 * Initialize a newly created constant visitor. The [evaluationEngine] is
1074 * used to evaluate instance creation expressions. The [lexicalEnvironment]
1075 * is a map containing values which should override identifiers, or `null` if
1076 * no overriding is necessary. The [_errorReporter] is used to report errors
1077 * found during evaluation. The [validator] is used by unit tests to verify
1078 * correct dependency analysis.
1079 */
1080 ConstantVisitor(this.evaluationEngine, this._errorReporter,
1081 {HashMap<String, DartObjectImpl> lexicalEnvironment})
1082 : _lexicalEnvironment = lexicalEnvironment {
1083 this._dartObjectComputer =
1084 new DartObjectComputer(_errorReporter, evaluationEngine.typeProvider);
1085 }
1086
1087 /**
1088 * Convenience getter to gain access to the [evalationEngine]'s type
1089 * provider.
1090 */
1091 TypeProvider get _typeProvider => evaluationEngine.typeProvider;
1092
1093 /**
1094 * Convenience getter to gain access to the [evaluationEngine]'s type system.
1095 */
1096 TypeSystem get _typeSystem => evaluationEngine.typeSystem;
1097
1098 @override
1099 DartObjectImpl visitAdjacentStrings(AdjacentStrings node) {
1100 DartObjectImpl result = null;
1101 for (StringLiteral string in node.strings) {
1102 if (result == null) {
1103 result = string.accept(this);
1104 } else {
1105 result =
1106 _dartObjectComputer.concatenate(node, result, string.accept(this));
1107 }
1108 }
1109 return result;
1110 }
1111
1112 @override
1113 DartObjectImpl visitBinaryExpression(BinaryExpression node) {
1114 DartObjectImpl leftResult = node.leftOperand.accept(this);
1115 DartObjectImpl rightResult = node.rightOperand.accept(this);
1116 TokenType operatorType = node.operator.type;
1117 // 'null' is almost never good operand
1118 if (operatorType != TokenType.BANG_EQ &&
1119 operatorType != TokenType.EQ_EQ &&
1120 operatorType != TokenType.QUESTION_QUESTION) {
1121 if (leftResult != null && leftResult.isNull ||
1122 rightResult != null && rightResult.isNull) {
1123 _error(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
1124 return null;
1125 }
1126 }
1127 // evaluate operator
1128 while (true) {
1129 if (operatorType == TokenType.AMPERSAND) {
1130 return _dartObjectComputer.bitAnd(node, leftResult, rightResult);
1131 } else if (operatorType == TokenType.AMPERSAND_AMPERSAND) {
1132 return _dartObjectComputer.logicalAnd(node, leftResult, rightResult);
1133 } else if (operatorType == TokenType.BANG_EQ) {
1134 return _dartObjectComputer.notEqual(node, leftResult, rightResult);
1135 } else if (operatorType == TokenType.BAR) {
1136 return _dartObjectComputer.bitOr(node, leftResult, rightResult);
1137 } else if (operatorType == TokenType.BAR_BAR) {
1138 return _dartObjectComputer.logicalOr(node, leftResult, rightResult);
1139 } else if (operatorType == TokenType.CARET) {
1140 return _dartObjectComputer.bitXor(node, leftResult, rightResult);
1141 } else if (operatorType == TokenType.EQ_EQ) {
1142 return _dartObjectComputer.equalEqual(node, leftResult, rightResult);
1143 } else if (operatorType == TokenType.GT) {
1144 return _dartObjectComputer.greaterThan(node, leftResult, rightResult);
1145 } else if (operatorType == TokenType.GT_EQ) {
1146 return _dartObjectComputer.greaterThanOrEqual(
1147 node, leftResult, rightResult);
1148 } else if (operatorType == TokenType.GT_GT) {
1149 return _dartObjectComputer.shiftRight(node, leftResult, rightResult);
1150 } else if (operatorType == TokenType.LT) {
1151 return _dartObjectComputer.lessThan(node, leftResult, rightResult);
1152 } else if (operatorType == TokenType.LT_EQ) {
1153 return _dartObjectComputer.lessThanOrEqual(
1154 node, leftResult, rightResult);
1155 } else if (operatorType == TokenType.LT_LT) {
1156 return _dartObjectComputer.shiftLeft(node, leftResult, rightResult);
1157 } else if (operatorType == TokenType.MINUS) {
1158 return _dartObjectComputer.minus(node, leftResult, rightResult);
1159 } else if (operatorType == TokenType.PERCENT) {
1160 return _dartObjectComputer.remainder(node, leftResult, rightResult);
1161 } else if (operatorType == TokenType.PLUS) {
1162 return _dartObjectComputer.add(node, leftResult, rightResult);
1163 } else if (operatorType == TokenType.STAR) {
1164 return _dartObjectComputer.times(node, leftResult, rightResult);
1165 } else if (operatorType == TokenType.SLASH) {
1166 return _dartObjectComputer.divide(node, leftResult, rightResult);
1167 } else if (operatorType == TokenType.TILDE_SLASH) {
1168 return _dartObjectComputer.integerDivide(node, leftResult, rightResult);
1169 } else if (operatorType == TokenType.QUESTION_QUESTION) {
1170 return _dartObjectComputer.questionQuestion(
1171 node, leftResult, rightResult);
1172 } else {
1173 // TODO(brianwilkerson) Figure out which error to report.
1174 _error(node, null);
1175 return null;
1176 }
1177 break;
1178 }
1179 }
1180
1181 @override
1182 DartObjectImpl visitBooleanLiteral(BooleanLiteral node) =>
1183 new DartObjectImpl(_typeProvider.boolType, BoolState.from(node.value));
1184
1185 @override
1186 DartObjectImpl visitConditionalExpression(ConditionalExpression node) {
1187 Expression condition = node.condition;
1188 DartObjectImpl conditionResult = condition.accept(this);
1189 DartObjectImpl thenResult = node.thenExpression.accept(this);
1190 DartObjectImpl elseResult = node.elseExpression.accept(this);
1191 if (conditionResult == null) {
1192 return conditionResult;
1193 } else if (!conditionResult.isBool) {
1194 _errorReporter.reportErrorForNode(
1195 CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL, condition);
1196 return null;
1197 } else if (thenResult == null) {
1198 return thenResult;
1199 } else if (elseResult == null) {
1200 return elseResult;
1201 }
1202 conditionResult =
1203 _dartObjectComputer.applyBooleanConversion(condition, conditionResult);
1204 if (conditionResult == null) {
1205 return conditionResult;
1206 }
1207 if (conditionResult.toBoolValue() == true) {
1208 return thenResult;
1209 } else if (conditionResult.toBoolValue() == false) {
1210 return elseResult;
1211 }
1212 ParameterizedType thenType = thenResult.type;
1213 ParameterizedType elseType = elseResult.type;
1214 return new DartObjectImpl.validWithUnknownValue(
1215 _typeSystem.getLeastUpperBound(_typeProvider, thenType, elseType)
1216 as InterfaceType);
1217 }
1218
1219 @override
1220 DartObjectImpl visitDoubleLiteral(DoubleLiteral node) =>
1221 new DartObjectImpl(_typeProvider.doubleType, new DoubleState(node.value));
1222
1223 @override
1224 DartObjectImpl visitInstanceCreationExpression(
1225 InstanceCreationExpression node) {
1226 if (!node.isConst) {
1227 // TODO(brianwilkerson) Figure out which error to report.
1228 _error(node, null);
1229 return null;
1230 }
1231 ConstructorElement constructor = node.staticElement;
1232 if (constructor == null) {
1233 // Couldn't resolve the constructor so we can't compute a value. No
1234 // problem - the error has already been reported.
1235 return null;
1236 }
1237 return evaluationEngine.evaluateConstructorCall(
1238 node, node.argumentList.arguments, constructor, this, _errorReporter);
1239 }
1240
1241 @override
1242 DartObjectImpl visitIntegerLiteral(IntegerLiteral node) =>
1243 new DartObjectImpl(_typeProvider.intType, new IntState(node.value));
1244
1245 @override
1246 DartObjectImpl visitInterpolationExpression(InterpolationExpression node) {
1247 DartObjectImpl result = node.expression.accept(this);
1248 if (result != null && !result.isBoolNumStringOrNull) {
1249 _error(node, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
1250 return null;
1251 }
1252 return _dartObjectComputer.performToString(node, result);
1253 }
1254
1255 @override
1256 DartObjectImpl visitInterpolationString(InterpolationString node) =>
1257 new DartObjectImpl(_typeProvider.stringType, new StringState(node.value));
1258
1259 @override
1260 DartObjectImpl visitListLiteral(ListLiteral node) {
1261 if (node.constKeyword == null) {
1262 _errorReporter.reportErrorForNode(
1263 CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL, node);
1264 return null;
1265 }
1266 bool errorOccurred = false;
1267 List<DartObjectImpl> elements = new List<DartObjectImpl>();
1268 for (Expression element in node.elements) {
1269 DartObjectImpl elementResult = element.accept(this);
1270 if (elementResult == null) {
1271 errorOccurred = true;
1272 } else {
1273 elements.add(elementResult);
1274 }
1275 }
1276 if (errorOccurred) {
1277 return null;
1278 }
1279 DartType elementType = _typeProvider.dynamicType;
1280 if (node.typeArguments != null &&
1281 node.typeArguments.arguments.length == 1) {
1282 DartType type = node.typeArguments.arguments[0].type;
1283 if (type != null) {
1284 elementType = type;
1285 }
1286 }
1287 InterfaceType listType = _typeProvider.listType.instantiate([elementType]);
1288 return new DartObjectImpl(listType, new ListState(elements));
1289 }
1290
1291 @override
1292 DartObjectImpl visitMapLiteral(MapLiteral node) {
1293 if (node.constKeyword == null) {
1294 _errorReporter.reportErrorForNode(
1295 CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL, node);
1296 return null;
1297 }
1298 bool errorOccurred = false;
1299 LinkedHashMap<DartObjectImpl, DartObjectImpl> map =
1300 new LinkedHashMap<DartObjectImpl, DartObjectImpl>();
1301 for (MapLiteralEntry entry in node.entries) {
1302 DartObjectImpl keyResult = entry.key.accept(this);
1303 DartObjectImpl valueResult = entry.value.accept(this);
1304 if (keyResult == null || valueResult == null) {
1305 errorOccurred = true;
1306 } else {
1307 map[keyResult] = valueResult;
1308 }
1309 }
1310 if (errorOccurred) {
1311 return null;
1312 }
1313 DartType keyType = _typeProvider.dynamicType;
1314 DartType valueType = _typeProvider.dynamicType;
1315 if (node.typeArguments != null &&
1316 node.typeArguments.arguments.length == 2) {
1317 DartType keyTypeCandidate = node.typeArguments.arguments[0].type;
1318 if (keyTypeCandidate != null) {
1319 keyType = keyTypeCandidate;
1320 }
1321 DartType valueTypeCandidate = node.typeArguments.arguments[1].type;
1322 if (valueTypeCandidate != null) {
1323 valueType = valueTypeCandidate;
1324 }
1325 }
1326 InterfaceType mapType =
1327 _typeProvider.mapType.instantiate([keyType, valueType]);
1328 return new DartObjectImpl(mapType, new MapState(map));
1329 }
1330
1331 @override
1332 DartObjectImpl visitMethodInvocation(MethodInvocation node) {
1333 Element element = node.methodName.staticElement;
1334 if (element is FunctionElement) {
1335 FunctionElement function = element;
1336 if (function.name == "identical") {
1337 NodeList<Expression> arguments = node.argumentList.arguments;
1338 if (arguments.length == 2) {
1339 Element enclosingElement = function.enclosingElement;
1340 if (enclosingElement is CompilationUnitElement) {
1341 LibraryElement library = enclosingElement.library;
1342 if (library.isDartCore) {
1343 DartObjectImpl leftArgument = arguments[0].accept(this);
1344 DartObjectImpl rightArgument = arguments[1].accept(this);
1345 return _dartObjectComputer.isIdentical(
1346 node, leftArgument, rightArgument);
1347 }
1348 }
1349 }
1350 }
1351 }
1352 // TODO(brianwilkerson) Figure out which error to report.
1353 _error(node, null);
1354 return null;
1355 }
1356
1357 @override
1358 DartObjectImpl visitNamedExpression(NamedExpression node) =>
1359 node.expression.accept(this);
1360
1361 @override
1362 DartObjectImpl visitNode(AstNode node) {
1363 // TODO(brianwilkerson) Figure out which error to report.
1364 _error(node, null);
1365 return null;
1366 }
1367
1368 @override
1369 DartObjectImpl visitNullLiteral(NullLiteral node) => _typeProvider.nullObject;
1370
1371 @override
1372 DartObjectImpl visitParenthesizedExpression(ParenthesizedExpression node) =>
1373 node.expression.accept(this);
1374
1375 @override
1376 DartObjectImpl visitPrefixedIdentifier(PrefixedIdentifier node) {
1377 SimpleIdentifier prefixNode = node.prefix;
1378 Element prefixElement = prefixNode.staticElement;
1379 // String.length
1380 if (prefixElement is! PrefixElement && prefixElement is! ClassElement) {
1381 DartObjectImpl prefixResult = node.prefix.accept(this);
1382 if (_isStringLength(prefixResult, node.identifier)) {
1383 return prefixResult.stringLength(_typeProvider);
1384 }
1385 }
1386 // importPrefix.CONST
1387 if (prefixElement is! PrefixElement) {
1388 DartObjectImpl prefixResult = prefixNode.accept(this);
1389 if (prefixResult == null) {
1390 // The error has already been reported.
1391 return null;
1392 }
1393 }
1394 // validate prefixed identifier
1395 return _getConstantValue(node, node.staticElement);
1396 }
1397
1398 @override
1399 DartObjectImpl visitPrefixExpression(PrefixExpression node) {
1400 DartObjectImpl operand = node.operand.accept(this);
1401 if (operand != null && operand.isNull) {
1402 _error(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
1403 return null;
1404 }
1405 while (true) {
1406 if (node.operator.type == TokenType.BANG) {
1407 return _dartObjectComputer.logicalNot(node, operand);
1408 } else if (node.operator.type == TokenType.TILDE) {
1409 return _dartObjectComputer.bitNot(node, operand);
1410 } else if (node.operator.type == TokenType.MINUS) {
1411 return _dartObjectComputer.negated(node, operand);
1412 } else {
1413 // TODO(brianwilkerson) Figure out which error to report.
1414 _error(node, null);
1415 return null;
1416 }
1417 break;
1418 }
1419 }
1420
1421 @override
1422 DartObjectImpl visitPropertyAccess(PropertyAccess node) {
1423 if (node.target != null) {
1424 DartObjectImpl prefixResult = node.target.accept(this);
1425 if (_isStringLength(prefixResult, node.propertyName)) {
1426 return prefixResult.stringLength(_typeProvider);
1427 }
1428 }
1429 return _getConstantValue(node, node.propertyName.staticElement);
1430 }
1431
1432 @override
1433 DartObjectImpl visitSimpleIdentifier(SimpleIdentifier node) {
1434 if (_lexicalEnvironment != null &&
1435 _lexicalEnvironment.containsKey(node.name)) {
1436 return _lexicalEnvironment[node.name];
1437 }
1438 return _getConstantValue(node, node.staticElement);
1439 }
1440
1441 @override
1442 DartObjectImpl visitSimpleStringLiteral(SimpleStringLiteral node) =>
1443 new DartObjectImpl(_typeProvider.stringType, new StringState(node.value));
1444
1445 @override
1446 DartObjectImpl visitStringInterpolation(StringInterpolation node) {
1447 DartObjectImpl result = null;
1448 bool first = true;
1449 for (InterpolationElement element in node.elements) {
1450 if (first) {
1451 result = element.accept(this);
1452 first = false;
1453 } else {
1454 result =
1455 _dartObjectComputer.concatenate(node, result, element.accept(this));
1456 }
1457 }
1458 return result;
1459 }
1460
1461 @override
1462 DartObjectImpl visitSymbolLiteral(SymbolLiteral node) {
1463 StringBuffer buffer = new StringBuffer();
1464 List<Token> components = node.components;
1465 for (int i = 0; i < components.length; i++) {
1466 if (i > 0) {
1467 buffer.writeCharCode(0x2E);
1468 }
1469 buffer.write(components[i].lexeme);
1470 }
1471 return new DartObjectImpl(
1472 _typeProvider.symbolType, new SymbolState(buffer.toString()));
1473 }
1474
1475 /**
1476 * Create an error associated with the given [node]. The error will have the
1477 * given error [code].
1478 */
1479 void _error(AstNode node, ErrorCode code) {
1480 _errorReporter.reportErrorForNode(
1481 code == null ? CompileTimeErrorCode.INVALID_CONSTANT : code, node);
1482 }
1483
1484 /**
1485 * Return the constant value of the static constant represented by the given
1486 * [element]. The [node] is the node to be used if an error needs to be
1487 * reported.
1488 */
1489 DartObjectImpl _getConstantValue(AstNode node, Element element) {
1490 if (element is PropertyAccessorElement) {
1491 element = (element as PropertyAccessorElement).variable;
1492 }
1493 if (element is VariableElementImpl) {
1494 VariableElementImpl variableElementImpl = element;
1495 evaluationEngine.validator.beforeGetEvaluationResult(element);
1496 EvaluationResultImpl value = variableElementImpl.evaluationResult;
1497 if (variableElementImpl.isConst && value != null) {
1498 return value.value;
1499 }
1500 } else if (element is ExecutableElement) {
1501 ExecutableElement function = element;
1502 if (function.isStatic) {
1503 ParameterizedType functionType = function.type;
1504 if (functionType == null) {
1505 functionType = _typeProvider.functionType;
1506 }
1507 return new DartObjectImpl(functionType, new FunctionState(function));
1508 }
1509 } else if (element is ClassElement ||
1510 element is FunctionTypeAliasElement ||
1511 element is DynamicElementImpl) {
1512 return new DartObjectImpl(_typeProvider.typeType, new TypeState(element));
1513 }
1514 // TODO(brianwilkerson) Figure out which error to report.
1515 _error(node, null);
1516 return null;
1517 }
1518
1519 /**
1520 * Return `true` if the given [targetResult] represents a string and the
1521 * [identifier] is "length".
1522 */
1523 bool _isStringLength(
1524 DartObjectImpl targetResult, SimpleIdentifier identifier) {
1525 if (targetResult == null || targetResult.type != _typeProvider.stringType) {
1526 return false;
1527 }
1528 return identifier.name == 'length';
1529 }
1530
1531 /**
1532 * Return the value of the given [expression], or a representation of 'null'
1533 * if the expression cannot be evaluated.
1534 */
1535 DartObjectImpl _valueOf(Expression expression) {
1536 DartObjectImpl expressionValue = expression.accept(this);
1537 if (expressionValue != null) {
1538 return expressionValue;
1539 }
1540 return _typeProvider.nullObject;
1541 }
1542 }
1543
1544 /**
1545 * A utility class that contains methods for manipulating instances of a Dart
1546 * class and for collecting errors during evaluation.
1547 */
1548 class DartObjectComputer {
1549 /**
1550 * The error reporter that we are using to collect errors.
1551 */
1552 final ErrorReporter _errorReporter;
1553
1554 /**
1555 * The type provider used to create objects of the appropriate types, and to
1556 * identify when an object is of a built-in type.
1557 */
1558 final TypeProvider _typeProvider;
1559
1560 DartObjectComputer(this._errorReporter, this._typeProvider);
1561
1562 DartObjectImpl add(BinaryExpression node, DartObjectImpl leftOperand,
1563 DartObjectImpl rightOperand) {
1564 if (leftOperand != null && rightOperand != null) {
1565 try {
1566 return leftOperand.add(_typeProvider, rightOperand);
1567 } on EvaluationException catch (exception) {
1568 _errorReporter.reportErrorForNode(exception.errorCode, node);
1569 return null;
1570 }
1571 }
1572 return null;
1573 }
1574
1575 /**
1576 * Return the result of applying boolean conversion to the [evaluationResult].
1577 * The [node] is the node against which errors should be reported.
1578 */
1579 DartObjectImpl applyBooleanConversion(
1580 AstNode node, DartObjectImpl evaluationResult) {
1581 if (evaluationResult != null) {
1582 try {
1583 return evaluationResult.convertToBool(_typeProvider);
1584 } on EvaluationException catch (exception) {
1585 _errorReporter.reportErrorForNode(exception.errorCode, node);
1586 }
1587 }
1588 return null;
1589 }
1590
1591 DartObjectImpl bitAnd(BinaryExpression node, DartObjectImpl leftOperand,
1592 DartObjectImpl rightOperand) {
1593 if (leftOperand != null && rightOperand != null) {
1594 try {
1595 return leftOperand.bitAnd(_typeProvider, rightOperand);
1596 } on EvaluationException catch (exception) {
1597 _errorReporter.reportErrorForNode(exception.errorCode, node);
1598 }
1599 }
1600 return null;
1601 }
1602
1603 DartObjectImpl bitNot(Expression node, DartObjectImpl evaluationResult) {
1604 if (evaluationResult != null) {
1605 try {
1606 return evaluationResult.bitNot(_typeProvider);
1607 } on EvaluationException catch (exception) {
1608 _errorReporter.reportErrorForNode(exception.errorCode, node);
1609 }
1610 }
1611 return null;
1612 }
1613
1614 DartObjectImpl bitOr(BinaryExpression node, DartObjectImpl leftOperand,
1615 DartObjectImpl rightOperand) {
1616 if (leftOperand != null && rightOperand != null) {
1617 try {
1618 return leftOperand.bitOr(_typeProvider, rightOperand);
1619 } on EvaluationException catch (exception) {
1620 _errorReporter.reportErrorForNode(exception.errorCode, node);
1621 }
1622 }
1623 return null;
1624 }
1625
1626 DartObjectImpl bitXor(BinaryExpression node, DartObjectImpl leftOperand,
1627 DartObjectImpl rightOperand) {
1628 if (leftOperand != null && rightOperand != null) {
1629 try {
1630 return leftOperand.bitXor(_typeProvider, rightOperand);
1631 } on EvaluationException catch (exception) {
1632 _errorReporter.reportErrorForNode(exception.errorCode, node);
1633 }
1634 }
1635 return null;
1636 }
1637
1638 DartObjectImpl concatenate(Expression node, DartObjectImpl leftOperand,
1639 DartObjectImpl rightOperand) {
1640 if (leftOperand != null && rightOperand != null) {
1641 try {
1642 return leftOperand.concatenate(_typeProvider, rightOperand);
1643 } on EvaluationException catch (exception) {
1644 _errorReporter.reportErrorForNode(exception.errorCode, node);
1645 }
1646 }
1647 return null;
1648 }
1649
1650 DartObjectImpl divide(BinaryExpression node, DartObjectImpl leftOperand,
1651 DartObjectImpl rightOperand) {
1652 if (leftOperand != null && rightOperand != null) {
1653 try {
1654 return leftOperand.divide(_typeProvider, rightOperand);
1655 } on EvaluationException catch (exception) {
1656 _errorReporter.reportErrorForNode(exception.errorCode, node);
1657 }
1658 }
1659 return null;
1660 }
1661
1662 DartObjectImpl equalEqual(Expression node, DartObjectImpl leftOperand,
1663 DartObjectImpl rightOperand) {
1664 if (leftOperand != null && rightOperand != null) {
1665 try {
1666 return leftOperand.equalEqual(_typeProvider, rightOperand);
1667 } on EvaluationException catch (exception) {
1668 _errorReporter.reportErrorForNode(exception.errorCode, node);
1669 }
1670 }
1671 return null;
1672 }
1673
1674 DartObjectImpl greaterThan(BinaryExpression node, DartObjectImpl leftOperand,
1675 DartObjectImpl rightOperand) {
1676 if (leftOperand != null && rightOperand != null) {
1677 try {
1678 return leftOperand.greaterThan(_typeProvider, rightOperand);
1679 } on EvaluationException catch (exception) {
1680 _errorReporter.reportErrorForNode(exception.errorCode, node);
1681 }
1682 }
1683 return null;
1684 }
1685
1686 DartObjectImpl greaterThanOrEqual(BinaryExpression node,
1687 DartObjectImpl leftOperand, DartObjectImpl rightOperand) {
1688 if (leftOperand != null && rightOperand != null) {
1689 try {
1690 return leftOperand.greaterThanOrEqual(_typeProvider, rightOperand);
1691 } on EvaluationException catch (exception) {
1692 _errorReporter.reportErrorForNode(exception.errorCode, node);
1693 }
1694 }
1695 return null;
1696 }
1697
1698 DartObjectImpl integerDivide(BinaryExpression node,
1699 DartObjectImpl leftOperand, DartObjectImpl rightOperand) {
1700 if (leftOperand != null && rightOperand != null) {
1701 try {
1702 return leftOperand.integerDivide(_typeProvider, rightOperand);
1703 } on EvaluationException catch (exception) {
1704 _errorReporter.reportErrorForNode(exception.errorCode, node);
1705 }
1706 }
1707 return null;
1708 }
1709
1710 DartObjectImpl isIdentical(Expression node, DartObjectImpl leftOperand,
1711 DartObjectImpl rightOperand) {
1712 if (leftOperand != null && rightOperand != null) {
1713 try {
1714 return leftOperand.isIdentical(_typeProvider, rightOperand);
1715 } on EvaluationException catch (exception) {
1716 _errorReporter.reportErrorForNode(exception.errorCode, node);
1717 }
1718 }
1719 return null;
1720 }
1721
1722 DartObjectImpl lessThan(BinaryExpression node, DartObjectImpl leftOperand,
1723 DartObjectImpl rightOperand) {
1724 if (leftOperand != null && rightOperand != null) {
1725 try {
1726 return leftOperand.lessThan(_typeProvider, rightOperand);
1727 } on EvaluationException catch (exception) {
1728 _errorReporter.reportErrorForNode(exception.errorCode, node);
1729 }
1730 }
1731 return null;
1732 }
1733
1734 DartObjectImpl lessThanOrEqual(BinaryExpression node,
1735 DartObjectImpl leftOperand, DartObjectImpl rightOperand) {
1736 if (leftOperand != null && rightOperand != null) {
1737 try {
1738 return leftOperand.lessThanOrEqual(_typeProvider, rightOperand);
1739 } on EvaluationException catch (exception) {
1740 _errorReporter.reportErrorForNode(exception.errorCode, node);
1741 }
1742 }
1743 return null;
1744 }
1745
1746 DartObjectImpl logicalAnd(BinaryExpression node, DartObjectImpl leftOperand,
1747 DartObjectImpl rightOperand) {
1748 if (leftOperand != null && rightOperand != null) {
1749 try {
1750 return leftOperand.logicalAnd(_typeProvider, rightOperand);
1751 } on EvaluationException catch (exception) {
1752 _errorReporter.reportErrorForNode(exception.errorCode, node);
1753 }
1754 }
1755 return null;
1756 }
1757
1758 DartObjectImpl logicalNot(Expression node, DartObjectImpl evaluationResult) {
1759 if (evaluationResult != null) {
1760 try {
1761 return evaluationResult.logicalNot(_typeProvider);
1762 } on EvaluationException catch (exception) {
1763 _errorReporter.reportErrorForNode(exception.errorCode, node);
1764 }
1765 }
1766 return null;
1767 }
1768
1769 DartObjectImpl logicalOr(BinaryExpression node, DartObjectImpl leftOperand,
1770 DartObjectImpl rightOperand) {
1771 if (leftOperand != null && rightOperand != null) {
1772 try {
1773 return leftOperand.logicalOr(_typeProvider, rightOperand);
1774 } on EvaluationException catch (exception) {
1775 _errorReporter.reportErrorForNode(exception.errorCode, node);
1776 }
1777 }
1778 return null;
1779 }
1780
1781 DartObjectImpl minus(BinaryExpression node, DartObjectImpl leftOperand,
1782 DartObjectImpl rightOperand) {
1783 if (leftOperand != null && rightOperand != null) {
1784 try {
1785 return leftOperand.minus(_typeProvider, rightOperand);
1786 } on EvaluationException catch (exception) {
1787 _errorReporter.reportErrorForNode(exception.errorCode, node);
1788 }
1789 }
1790 return null;
1791 }
1792
1793 DartObjectImpl negated(Expression node, DartObjectImpl evaluationResult) {
1794 if (evaluationResult != null) {
1795 try {
1796 return evaluationResult.negated(_typeProvider);
1797 } on EvaluationException catch (exception) {
1798 _errorReporter.reportErrorForNode(exception.errorCode, node);
1799 }
1800 }
1801 return null;
1802 }
1803
1804 DartObjectImpl notEqual(BinaryExpression node, DartObjectImpl leftOperand,
1805 DartObjectImpl rightOperand) {
1806 if (leftOperand != null && rightOperand != null) {
1807 try {
1808 return leftOperand.notEqual(_typeProvider, rightOperand);
1809 } on EvaluationException catch (exception) {
1810 _errorReporter.reportErrorForNode(exception.errorCode, node);
1811 }
1812 }
1813 return null;
1814 }
1815
1816 DartObjectImpl performToString(
1817 AstNode node, DartObjectImpl evaluationResult) {
1818 if (evaluationResult != null) {
1819 try {
1820 return evaluationResult.performToString(_typeProvider);
1821 } on EvaluationException catch (exception) {
1822 _errorReporter.reportErrorForNode(exception.errorCode, node);
1823 }
1824 }
1825 return null;
1826 }
1827
1828 DartObjectImpl questionQuestion(Expression node, DartObjectImpl leftOperand,
1829 DartObjectImpl rightOperand) {
1830 if (leftOperand != null && rightOperand != null) {
1831 if (leftOperand.isNull) {
1832 return rightOperand;
1833 }
1834 return leftOperand;
1835 }
1836 return null;
1837 }
1838
1839 DartObjectImpl remainder(BinaryExpression node, DartObjectImpl leftOperand,
1840 DartObjectImpl rightOperand) {
1841 if (leftOperand != null && rightOperand != null) {
1842 try {
1843 return leftOperand.remainder(_typeProvider, rightOperand);
1844 } on EvaluationException catch (exception) {
1845 _errorReporter.reportErrorForNode(exception.errorCode, node);
1846 }
1847 }
1848 return null;
1849 }
1850
1851 DartObjectImpl shiftLeft(BinaryExpression node, DartObjectImpl leftOperand,
1852 DartObjectImpl rightOperand) {
1853 if (leftOperand != null && rightOperand != null) {
1854 try {
1855 return leftOperand.shiftLeft(_typeProvider, rightOperand);
1856 } on EvaluationException catch (exception) {
1857 _errorReporter.reportErrorForNode(exception.errorCode, node);
1858 }
1859 }
1860 return null;
1861 }
1862
1863 DartObjectImpl shiftRight(BinaryExpression node, DartObjectImpl leftOperand,
1864 DartObjectImpl rightOperand) {
1865 if (leftOperand != null && rightOperand != null) {
1866 try {
1867 return leftOperand.shiftRight(_typeProvider, rightOperand);
1868 } on EvaluationException catch (exception) {
1869 _errorReporter.reportErrorForNode(exception.errorCode, node);
1870 }
1871 }
1872 return null;
1873 }
1874
1875 /**
1876 * Return the result of invoking the 'length' getter on the
1877 * [evaluationResult]. The [node] is the node against which errors should be
1878 * reported.
1879 */
1880 EvaluationResultImpl stringLength(
1881 Expression node, EvaluationResultImpl evaluationResult) {
1882 if (evaluationResult.value != null) {
1883 try {
1884 return new EvaluationResultImpl(
1885 evaluationResult.value.stringLength(_typeProvider));
1886 } on EvaluationException catch (exception) {
1887 _errorReporter.reportErrorForNode(exception.errorCode, node);
1888 }
1889 }
1890 return new EvaluationResultImpl(null);
1891 }
1892
1893 DartObjectImpl times(BinaryExpression node, DartObjectImpl leftOperand,
1894 DartObjectImpl rightOperand) {
1895 if (leftOperand != null && rightOperand != null) {
1896 try {
1897 return leftOperand.times(_typeProvider, rightOperand);
1898 } on EvaluationException catch (exception) {
1899 _errorReporter.reportErrorForNode(exception.errorCode, node);
1900 }
1901 }
1902 return null;
1903 }
1904 }
1905
1906 /**
1907 * The result of attempting to evaluate an expression.
1908 */
1909 class EvaluationResult {
1910 // TODO(brianwilkerson) Merge with EvaluationResultImpl
1911 /**
1912 * The value of the expression.
1913 */
1914 final DartObject value;
1915
1916 /**
1917 * The errors that should be reported for the expression(s) that were
1918 * evaluated.
1919 */
1920 final List<AnalysisError> _errors;
1921
1922 /**
1923 * Initialize a newly created result object with the given [value] and set of
1924 * [_errors]. Clients should use one of the factory methods: [forErrors] and
1925 * [forValue].
1926 */
1927 EvaluationResult(this.value, this._errors);
1928
1929 /**
1930 * Return a list containing the errors that should be reported for the
1931 * expression(s) that were evaluated. If there are no such errors, the list
1932 * will be empty. The list can be empty even if the expression is not a valid
1933 * compile time constant if the errors would have been reported by other parts
1934 * of the analysis engine.
1935 */
1936 List<AnalysisError> get errors =>
1937 _errors == null ? AnalysisError.NO_ERRORS : _errors;
1938
1939 /**
1940 * Return `true` if the expression is a compile-time constant expression that
1941 * would not throw an exception when evaluated.
1942 */
1943 bool get isValid => _errors == null;
1944
1945 /**
1946 * Return an evaluation result representing the result of evaluating an
1947 * expression that is not a compile-time constant because of the given
1948 * [errors].
1949 */
1950 static EvaluationResult forErrors(List<AnalysisError> errors) =>
1951 new EvaluationResult(null, errors);
1952
1953 /**
1954 * Return an evaluation result representing the result of evaluating an
1955 * expression that is a compile-time constant that evaluates to the given
1956 * [value].
1957 */
1958 static EvaluationResult forValue(DartObject value) =>
1959 new EvaluationResult(value, null);
1960 }
1961
1962 /**
1963 * The result of attempting to evaluate a expression.
1964 */
1965 class EvaluationResultImpl {
1966 /**
1967 * The errors encountered while trying to evaluate the compile time constant.
1968 * These errors may or may not have prevented the expression from being a
1969 * valid compile time constant.
1970 */
1971 List<AnalysisError> _errors;
1972
1973 /**
1974 * The value of the expression, or `null` if the value couldn't be computed
1975 * due to errors.
1976 */
1977 final DartObjectImpl value;
1978
1979 EvaluationResultImpl(this.value, [List<AnalysisError> errors]) {
1980 this._errors = errors == null ? <AnalysisError>[] : errors;
1981 }
1982
1983 List<AnalysisError> get errors => _errors;
1984
1985 bool equalValues(TypeProvider typeProvider, EvaluationResultImpl result) {
1986 if (this.value != null) {
1987 if (result.value == null) {
1988 return false;
1989 }
1990 return value == result.value;
1991 } else {
1992 return false;
1993 }
1994 }
1995
1996 @override
1997 String toString() {
1998 if (value == null) {
1999 return "error";
2000 }
2001 return value.toString();
2002 }
2003 }
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/context/declared_variables.dart ('k') | pkg/analyzer/lib/src/dart/constant/utilities.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698