OLD | NEW |
| (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 // This code was auto-generated, is not intended to be edited, and is subject to | |
6 // significant change. Please see the README file for more information. | |
7 | |
8 library engine.constant; | |
9 | |
10 import 'dart:collection'; | |
11 | |
12 import 'package:analyzer/src/generated/engine.dart'; | |
13 import 'package:analyzer/src/generated/utilities_general.dart'; | |
14 import 'package:analyzer/src/task/dart.dart'; | |
15 | |
16 import 'ast.dart'; | |
17 import 'element.dart'; | |
18 import 'engine.dart' show AnalysisEngine, RecordingErrorListener; | |
19 import 'error.dart'; | |
20 import 'java_core.dart'; | |
21 import 'resolver.dart' show TypeProvider, TypeSystem, TypeSystemImpl; | |
22 import 'scanner.dart' show Token, TokenType; | |
23 import 'source.dart' show Source; | |
24 import 'utilities_collection.dart'; | |
25 import 'utilities_dart.dart' show ParameterKind; | |
26 | |
27 /** | |
28 * Callback used by [ReferenceFinder] to report that a dependency was found. | |
29 */ | |
30 typedef void ReferenceFinderCallback(ConstantEvaluationTarget dependency); | |
31 | |
32 /** | |
33 * The state of an object representing a boolean value. | |
34 */ | |
35 class BoolState extends InstanceState { | |
36 /** | |
37 * An instance representing the boolean value 'false'. | |
38 */ | |
39 static BoolState FALSE_STATE = new BoolState(false); | |
40 | |
41 /** | |
42 * An instance representing the boolean value 'true'. | |
43 */ | |
44 static BoolState TRUE_STATE = new BoolState(true); | |
45 | |
46 /** | |
47 * A state that can be used to represent a boolean whose value is not known. | |
48 */ | |
49 static BoolState UNKNOWN_VALUE = new BoolState(null); | |
50 | |
51 /** | |
52 * The value of this instance. | |
53 */ | |
54 final bool value; | |
55 | |
56 /** | |
57 * Initialize a newly created state to represent the given [value]. | |
58 */ | |
59 BoolState(this.value); | |
60 | |
61 @override | |
62 bool get hasExactValue => true; | |
63 | |
64 @override | |
65 int get hashCode => value == null ? 0 : (value ? 2 : 3); | |
66 | |
67 @override | |
68 bool get isBool => true; | |
69 | |
70 @override | |
71 bool get isBoolNumStringOrNull => true; | |
72 | |
73 @override | |
74 bool get isUnknown => value == null; | |
75 | |
76 @override | |
77 String get typeName => "bool"; | |
78 | |
79 @override | |
80 bool operator ==(Object object) => | |
81 object is BoolState && identical(value, object.value); | |
82 | |
83 @override | |
84 BoolState convertToBool() => this; | |
85 | |
86 @override | |
87 StringState convertToString() { | |
88 if (value == null) { | |
89 return StringState.UNKNOWN_VALUE; | |
90 } | |
91 return new StringState(value ? "true" : "false"); | |
92 } | |
93 | |
94 @override | |
95 BoolState equalEqual(InstanceState rightOperand) { | |
96 assertBoolNumStringOrNull(rightOperand); | |
97 return isIdentical(rightOperand); | |
98 } | |
99 | |
100 @override | |
101 BoolState isIdentical(InstanceState rightOperand) { | |
102 if (value == null) { | |
103 return UNKNOWN_VALUE; | |
104 } | |
105 if (rightOperand is BoolState) { | |
106 bool rightValue = rightOperand.value; | |
107 if (rightValue == null) { | |
108 return UNKNOWN_VALUE; | |
109 } | |
110 return BoolState.from(identical(value, rightValue)); | |
111 } else if (rightOperand is DynamicState) { | |
112 return UNKNOWN_VALUE; | |
113 } | |
114 return FALSE_STATE; | |
115 } | |
116 | |
117 @override | |
118 BoolState logicalAnd(InstanceState rightOperand) { | |
119 assertBool(rightOperand); | |
120 if (value == null) { | |
121 return UNKNOWN_VALUE; | |
122 } | |
123 return value ? rightOperand.convertToBool() : FALSE_STATE; | |
124 } | |
125 | |
126 @override | |
127 BoolState logicalNot() { | |
128 if (value == null) { | |
129 return UNKNOWN_VALUE; | |
130 } | |
131 return value ? FALSE_STATE : TRUE_STATE; | |
132 } | |
133 | |
134 @override | |
135 BoolState logicalOr(InstanceState rightOperand) { | |
136 assertBool(rightOperand); | |
137 if (value == null) { | |
138 return UNKNOWN_VALUE; | |
139 } | |
140 return value ? TRUE_STATE : rightOperand.convertToBool(); | |
141 } | |
142 | |
143 @override | |
144 String toString() => value == null ? "-unknown-" : (value ? "true" : "false"); | |
145 | |
146 /** | |
147 * Return the boolean state representing the given boolean [value]. | |
148 */ | |
149 static BoolState from(bool value) => | |
150 value ? BoolState.TRUE_STATE : BoolState.FALSE_STATE; | |
151 } | |
152 | |
153 /** | |
154 * An [AstCloner] that copies the necessary information from the AST to allow | |
155 * constants to be evaluated. | |
156 */ | |
157 class ConstantAstCloner extends AstCloner { | |
158 ConstantAstCloner() : super(true); | |
159 | |
160 @override | |
161 InstanceCreationExpression visitInstanceCreationExpression( | |
162 InstanceCreationExpression node) { | |
163 InstanceCreationExpression expression = | |
164 super.visitInstanceCreationExpression(node); | |
165 expression.staticElement = node.staticElement; | |
166 return expression; | |
167 } | |
168 | |
169 @override | |
170 RedirectingConstructorInvocation visitRedirectingConstructorInvocation( | |
171 RedirectingConstructorInvocation node) { | |
172 RedirectingConstructorInvocation invocation = | |
173 super.visitRedirectingConstructorInvocation(node); | |
174 invocation.staticElement = node.staticElement; | |
175 return invocation; | |
176 } | |
177 | |
178 @override | |
179 SimpleIdentifier visitSimpleIdentifier(SimpleIdentifier node) { | |
180 SimpleIdentifier identifier = super.visitSimpleIdentifier(node); | |
181 identifier.staticElement = node.staticElement; | |
182 return identifier; | |
183 } | |
184 | |
185 @override | |
186 SuperConstructorInvocation visitSuperConstructorInvocation( | |
187 SuperConstructorInvocation node) { | |
188 SuperConstructorInvocation invocation = | |
189 super.visitSuperConstructorInvocation(node); | |
190 invocation.staticElement = node.staticElement; | |
191 return invocation; | |
192 } | |
193 } | |
194 | |
195 /** | |
196 * Helper class encapsulating the methods for evaluating constants and | |
197 * constant instance creation expressions. | |
198 */ | |
199 class ConstantEvaluationEngine { | |
200 /** | |
201 * Parameter to "fromEnvironment" methods that denotes the default value. | |
202 */ | |
203 static String _DEFAULT_VALUE_PARAM = "defaultValue"; | |
204 | |
205 /** | |
206 * Source of RegExp matching any public identifier. | |
207 * From sdk/lib/internal/symbol.dart. | |
208 */ | |
209 static String _PUBLIC_IDENTIFIER_RE = | |
210 "(?!${ConstantValueComputer._RESERVED_WORD_RE}\\b(?!\\\$))[a-zA-Z\$][\\w\$
]*"; | |
211 | |
212 /** | |
213 * RegExp that validates a non-empty non-private symbol. | |
214 * From sdk/lib/internal/symbol.dart. | |
215 */ | |
216 static RegExp _PUBLIC_SYMBOL_PATTERN = new RegExp( | |
217 "^(?:${ConstantValueComputer._OPERATOR_RE}\$|$_PUBLIC_IDENTIFIER_RE(?:=?\$
|[.](?!\$)))+?\$"); | |
218 | |
219 /** | |
220 * The type system. This is used to gues the types of constants when their | |
221 * exact value is unknown. | |
222 */ | |
223 final TypeSystem typeSystem; | |
224 | |
225 /** | |
226 * The set of variables declared on the command line using '-D'. | |
227 */ | |
228 final DeclaredVariables _declaredVariables; | |
229 | |
230 /** | |
231 * Validator used to verify correct dependency analysis when running unit | |
232 * tests. | |
233 */ | |
234 final ConstantEvaluationValidator validator; | |
235 | |
236 /** | |
237 * Initialize a newly created [ConstantEvaluationEngine]. The [typeProvider] | |
238 * is used to access known types. [_declaredVariables] is the set of | |
239 * variables declared on the command line using '-D'. The [validator], if | |
240 * given, is used to verify correct dependency analysis when running unit | |
241 * tests. | |
242 */ | |
243 ConstantEvaluationEngine(TypeProvider typeProvider, this._declaredVariables, | |
244 {ConstantEvaluationValidator validator}) | |
245 : validator = validator != null | |
246 ? validator | |
247 : new ConstantEvaluationValidator_ForProduction(), | |
248 typeSystem = new TypeSystemImpl(typeProvider); | |
249 | |
250 /** | |
251 * The type provider used to access the known types. | |
252 */ | |
253 TypeProvider get typeProvider => typeSystem.typeProvider; | |
254 | |
255 /** | |
256 * Check that the arguments to a call to fromEnvironment() are correct. The | |
257 * [arguments] are the AST nodes of the arguments. The [argumentValues] are | |
258 * the values of the unnamed arguments. The [namedArgumentValues] are the | |
259 * values of the named arguments. The [expectedDefaultValueType] is the | |
260 * allowed type of the "defaultValue" parameter (if present). Note: | |
261 * "defaultValue" is always allowed to be null. Return `true` if the arguments | |
262 * are correct, `false` if there is an error. | |
263 */ | |
264 bool checkFromEnvironmentArguments(NodeList<Expression> arguments, | |
265 List<DartObjectImpl> argumentValues, | |
266 HashMap<String, DartObjectImpl> namedArgumentValues, | |
267 InterfaceType expectedDefaultValueType) { | |
268 int argumentCount = arguments.length; | |
269 if (argumentCount < 1 || argumentCount > 2) { | |
270 return false; | |
271 } | |
272 if (arguments[0] is NamedExpression) { | |
273 return false; | |
274 } | |
275 if (!identical(argumentValues[0].type, typeProvider.stringType)) { | |
276 return false; | |
277 } | |
278 if (argumentCount == 2) { | |
279 if (arguments[1] is! NamedExpression) { | |
280 return false; | |
281 } | |
282 if (!((arguments[1] as NamedExpression).name.label.name == | |
283 _DEFAULT_VALUE_PARAM)) { | |
284 return false; | |
285 } | |
286 ParameterizedType defaultValueType = | |
287 namedArgumentValues[_DEFAULT_VALUE_PARAM].type; | |
288 if (!(identical(defaultValueType, expectedDefaultValueType) || | |
289 identical(defaultValueType, typeProvider.nullType))) { | |
290 return false; | |
291 } | |
292 } | |
293 return true; | |
294 } | |
295 | |
296 /** | |
297 * Check that the arguments to a call to Symbol() are correct. The [arguments] | |
298 * are the AST nodes of the arguments. The [argumentValues] are the values of | |
299 * the unnamed arguments. The [namedArgumentValues] are the values of the | |
300 * named arguments. Return `true` if the arguments are correct, `false` if | |
301 * there is an error. | |
302 */ | |
303 bool checkSymbolArguments(NodeList<Expression> arguments, | |
304 List<DartObjectImpl> argumentValues, | |
305 HashMap<String, DartObjectImpl> namedArgumentValues) { | |
306 if (arguments.length != 1) { | |
307 return false; | |
308 } | |
309 if (arguments[0] is NamedExpression) { | |
310 return false; | |
311 } | |
312 if (!identical(argumentValues[0].type, typeProvider.stringType)) { | |
313 return false; | |
314 } | |
315 String name = argumentValues[0].stringValue; | |
316 return isValidPublicSymbol(name); | |
317 } | |
318 | |
319 /** | |
320 * Compute the constant value associated with the given [constant]. | |
321 */ | |
322 void computeConstantValue(ConstantEvaluationTarget constant) { | |
323 validator.beforeComputeValue(constant); | |
324 if (constant is ParameterElement) { | |
325 if (constant.initializer != null) { | |
326 Expression defaultValue = | |
327 (constant as PotentiallyConstVariableElement).constantInitializer; | |
328 if (defaultValue != null) { | |
329 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
330 ErrorReporter errorReporter = | |
331 new ErrorReporter(errorListener, constant.source); | |
332 DartObjectImpl dartObject = | |
333 defaultValue.accept(new ConstantVisitor(this, errorReporter)); | |
334 (constant as ParameterElementImpl).evaluationResult = | |
335 new EvaluationResultImpl(dartObject, errorListener.errors); | |
336 } | |
337 } | |
338 } else if (constant is VariableElement) { | |
339 Expression constantInitializer = | |
340 (constant as PotentiallyConstVariableElement).constantInitializer; | |
341 if (constantInitializer != null) { | |
342 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
343 ErrorReporter errorReporter = | |
344 new ErrorReporter(errorListener, constant.source); | |
345 DartObjectImpl dartObject = constantInitializer | |
346 .accept(new ConstantVisitor(this, errorReporter)); | |
347 // Only check the type for truly const declarations (don't check final | |
348 // fields with initializers, since their types may be generic. The type | |
349 // of the final field will be checked later, when the constructor is | |
350 // invoked). | |
351 if (dartObject != null && constant.isConst) { | |
352 if (!runtimeTypeMatch(dartObject, constant.type)) { | |
353 errorReporter.reportErrorForElement( | |
354 CheckedModeCompileTimeErrorCode.VARIABLE_TYPE_MISMATCH, | |
355 constant, [dartObject.type, constant.type]); | |
356 } | |
357 } | |
358 (constant as VariableElementImpl).evaluationResult = | |
359 new EvaluationResultImpl(dartObject, errorListener.errors); | |
360 } | |
361 } else if (constant is ConstructorElement) { | |
362 if (constant.isConst) { | |
363 // No evaluation needs to be done; constructor declarations are only in | |
364 // the dependency graph to ensure that any constants referred to in | |
365 // initializer lists and parameter defaults are evaluated before | |
366 // invocations of the constructor. However we do need to annotate the | |
367 // element as being free of constant evaluation cycles so that later | |
368 // code will know that it is safe to evaluate. | |
369 (constant as ConstructorElementImpl).isCycleFree = true; | |
370 } | |
371 } else if (constant is ConstantEvaluationTarget_Annotation) { | |
372 Annotation constNode = constant.annotation; | |
373 ElementAnnotationImpl elementAnnotation = constNode.elementAnnotation; | |
374 // elementAnnotation is null if the annotation couldn't be resolved, in | |
375 // which case we skip it. | |
376 if (elementAnnotation != null) { | |
377 Element element = elementAnnotation.element; | |
378 if (element is PropertyAccessorElement && | |
379 element.variable is VariableElementImpl) { | |
380 // The annotation is a reference to a compile-time constant variable. | |
381 // Just copy the evaluation result. | |
382 VariableElementImpl variableElement = | |
383 element.variable as VariableElementImpl; | |
384 if (variableElement.evaluationResult != null) { | |
385 elementAnnotation.evaluationResult = | |
386 variableElement.evaluationResult; | |
387 } else { | |
388 // This could happen in the event that the annotation refers to a | |
389 // non-constant. The error is detected elsewhere, so just silently | |
390 // ignore it here. | |
391 elementAnnotation.evaluationResult = new EvaluationResultImpl(null); | |
392 } | |
393 } else if (element is ConstructorElementImpl && | |
394 element.isConst && | |
395 constNode.arguments != null) { | |
396 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
397 CompilationUnit sourceCompilationUnit = | |
398 constNode.getAncestor((node) => node is CompilationUnit); | |
399 ErrorReporter errorReporter = new ErrorReporter( | |
400 errorListener, sourceCompilationUnit.element.source); | |
401 ConstantVisitor constantVisitor = | |
402 new ConstantVisitor(this, errorReporter); | |
403 DartObjectImpl result = evaluateConstructorCall(constNode, | |
404 constNode.arguments.arguments, element, constantVisitor, | |
405 errorReporter); | |
406 elementAnnotation.evaluationResult = | |
407 new EvaluationResultImpl(result, errorListener.errors); | |
408 } else { | |
409 // This may happen for invalid code (e.g. failing to pass arguments | |
410 // to an annotation which references a const constructor). The error | |
411 // is detected elsewhere, so just silently ignore it here. | |
412 elementAnnotation.evaluationResult = new EvaluationResultImpl(null); | |
413 } | |
414 } | |
415 } else { | |
416 // Should not happen. | |
417 assert(false); | |
418 AnalysisEngine.instance.logger.logError( | |
419 "Constant value computer trying to compute the value of a node of type
${constant.runtimeType}"); | |
420 return; | |
421 } | |
422 } | |
423 | |
424 /** | |
425 * Determine which constant elements need to have their values computed | |
426 * prior to computing the value of [constant], and report them using | |
427 * [callback]. | |
428 * | |
429 * Note that it's possible (in erroneous code) for a constant to depend on a | |
430 * non-constant. When this happens, we report the dependency anyhow so that | |
431 * if the non-constant changes to a constant, we will know to recompute the | |
432 * thing that depends on it. [computeDependencies] and | |
433 * [computeConstantValue] are responsible for ignoring the request if they | |
434 * are asked to act on a non-constant target. | |
435 */ | |
436 void computeDependencies( | |
437 ConstantEvaluationTarget constant, ReferenceFinderCallback callback) { | |
438 ReferenceFinder referenceFinder = new ReferenceFinder(callback); | |
439 if (constant is ParameterElement) { | |
440 if (constant.initializer != null) { | |
441 Expression defaultValue = | |
442 (constant as ConstVariableElement).constantInitializer; | |
443 if (defaultValue != null) { | |
444 defaultValue.accept(referenceFinder); | |
445 } | |
446 } | |
447 } else if (constant is PotentiallyConstVariableElement) { | |
448 Expression initializer = constant.constantInitializer; | |
449 if (initializer != null) { | |
450 initializer.accept(referenceFinder); | |
451 } | |
452 } else if (constant is ConstructorElementImpl) { | |
453 if (constant.isConst) { | |
454 constant.isCycleFree = false; | |
455 ConstructorElement redirectedConstructor = | |
456 getConstRedirectedConstructor(constant); | |
457 if (redirectedConstructor != null) { | |
458 ConstructorElement redirectedConstructorBase = | |
459 ConstantEvaluationEngine | |
460 ._getConstructorBase(redirectedConstructor); | |
461 callback(redirectedConstructorBase); | |
462 return; | |
463 } else if (constant.isFactory) { | |
464 // Factory constructor, but getConstRedirectedConstructor returned | |
465 // null. This can happen if we're visiting one of the special externa
l | |
466 // const factory constructors in the SDK, or if the code contains | |
467 // errors (such as delegating to a non-const constructor, or delegatin
g | |
468 // to a constructor that can't be resolved). In any of these cases, | |
469 // we'll evaluate calls to this constructor without having to refer to | |
470 // any other constants. So we don't need to report any dependencies. | |
471 return; | |
472 } | |
473 bool superInvocationFound = false; | |
474 List<ConstructorInitializer> initializers = | |
475 constant.constantInitializers; | |
476 for (ConstructorInitializer initializer in initializers) { | |
477 if (initializer is SuperConstructorInvocation) { | |
478 superInvocationFound = true; | |
479 } | |
480 initializer.accept(referenceFinder); | |
481 } | |
482 if (!superInvocationFound) { | |
483 // No explicit superconstructor invocation found, so we need to | |
484 // manually insert a reference to the implicit superconstructor. | |
485 InterfaceType superclass = | |
486 (constant.returnType as InterfaceType).superclass; | |
487 if (superclass != null && !superclass.isObject) { | |
488 ConstructorElement unnamedConstructor = ConstantEvaluationEngine | |
489 ._getConstructorBase(superclass.element.unnamedConstructor); | |
490 if (unnamedConstructor != null) { | |
491 callback(unnamedConstructor); | |
492 } | |
493 } | |
494 } | |
495 for (FieldElement field in constant.enclosingElement.fields) { | |
496 // Note: non-static const isn't allowed but we handle it anyway so | |
497 // that we won't be confused by incorrect code. | |
498 if ((field.isFinal || field.isConst) && | |
499 !field.isStatic && | |
500 field.initializer != null) { | |
501 callback(field); | |
502 } | |
503 } | |
504 for (ParameterElement parameterElement in constant.parameters) { | |
505 callback(parameterElement); | |
506 } | |
507 } | |
508 } else if (constant is ConstantEvaluationTarget_Annotation) { | |
509 Annotation constNode = constant.annotation; | |
510 ElementAnnotationImpl elementAnnotation = constNode.elementAnnotation; | |
511 // elementAnnotation is null if the annotation couldn't be resolved, in | |
512 // which case we skip it. | |
513 if (elementAnnotation != null) { | |
514 Element element = elementAnnotation.element; | |
515 if (element is PropertyAccessorElement && | |
516 element.variable is VariableElementImpl) { | |
517 // The annotation is a reference to a compile-time constant variable, | |
518 // so it depends on the variable. | |
519 callback(element.variable); | |
520 } else if (element is ConstructorElementImpl) { | |
521 // The annotation is a constructor invocation, so it depends on the | |
522 // constructor. | |
523 callback(element); | |
524 } else { | |
525 // This could happen in the event of invalid code. The error will be | |
526 // reported at constant evaluation time. | |
527 } | |
528 } | |
529 if (constNode.arguments != null) { | |
530 constNode.arguments.accept(referenceFinder); | |
531 } | |
532 } else { | |
533 // Should not happen. | |
534 assert(false); | |
535 AnalysisEngine.instance.logger.logError( | |
536 "Constant value computer trying to compute the value of a node of type
${constant.runtimeType}"); | |
537 } | |
538 } | |
539 | |
540 /** | |
541 * Evaluate a call to fromEnvironment() on the bool, int, or String class. The | |
542 * [environmentValue] is the value fetched from the environment. The | |
543 * [builtInDefaultValue] is the value that should be used as the default if no | |
544 * "defaultValue" argument appears in [namedArgumentValues]. The | |
545 * [namedArgumentValues] are the values of the named parameters passed to | |
546 * fromEnvironment(). Return a [DartObjectImpl] object corresponding to the | |
547 * evaluated result. | |
548 */ | |
549 DartObjectImpl computeValueFromEnvironment(DartObject environmentValue, | |
550 DartObjectImpl builtInDefaultValue, | |
551 HashMap<String, DartObjectImpl> namedArgumentValues) { | |
552 DartObjectImpl value = environmentValue as DartObjectImpl; | |
553 if (value.isUnknown || value.isNull) { | |
554 // The name either doesn't exist in the environment or we couldn't parse | |
555 // the corresponding value. | |
556 // If the code supplied an explicit default, use it. | |
557 if (namedArgumentValues.containsKey(_DEFAULT_VALUE_PARAM)) { | |
558 value = namedArgumentValues[_DEFAULT_VALUE_PARAM]; | |
559 } else if (value.isNull) { | |
560 // The code didn't supply an explicit default. | |
561 // The name exists in the environment but we couldn't parse the | |
562 // corresponding value. | |
563 // So use the built-in default value, because this is what the VM does. | |
564 value = builtInDefaultValue; | |
565 } else { | |
566 // The code didn't supply an explicit default. | |
567 // The name doesn't exist in the environment. | |
568 // The VM would use the built-in default value, but we don't want to do | |
569 // that for analysis because it's likely to lead to cascading errors. | |
570 // So just leave [value] in the unknown state. | |
571 } | |
572 } | |
573 return value; | |
574 } | |
575 | |
576 DartObjectImpl evaluateConstructorCall(AstNode node, | |
577 NodeList<Expression> arguments, ConstructorElement constructor, | |
578 ConstantVisitor constantVisitor, ErrorReporter errorReporter) { | |
579 if (!_getConstructorBase(constructor).isCycleFree) { | |
580 // It's not safe to evaluate this constructor, so bail out. | |
581 // TODO(paulberry): ensure that a reasonable error message is produced | |
582 // in this case, as well as other cases involving constant expression | |
583 // circularities (e.g. "compile-time constant expression depends on | |
584 // itself") | |
585 return new DartObjectImpl.validWithUnknownValue(constructor.returnType); | |
586 } | |
587 int argumentCount = arguments.length; | |
588 List<DartObjectImpl> argumentValues = | |
589 new List<DartObjectImpl>(argumentCount); | |
590 List<Expression> argumentNodes = new List<Expression>(argumentCount); | |
591 HashMap<String, DartObjectImpl> namedArgumentValues = | |
592 new HashMap<String, DartObjectImpl>(); | |
593 HashMap<String, NamedExpression> namedArgumentNodes = | |
594 new HashMap<String, NamedExpression>(); | |
595 for (int i = 0; i < argumentCount; i++) { | |
596 Expression argument = arguments[i]; | |
597 if (argument is NamedExpression) { | |
598 String name = argument.name.label.name; | |
599 namedArgumentValues[name] = | |
600 constantVisitor._valueOf(argument.expression); | |
601 namedArgumentNodes[name] = argument; | |
602 argumentValues[i] = typeProvider.nullObject; | |
603 } else { | |
604 argumentValues[i] = constantVisitor._valueOf(argument); | |
605 argumentNodes[i] = argument; | |
606 } | |
607 } | |
608 constructor = followConstantRedirectionChain(constructor); | |
609 InterfaceType definingClass = constructor.returnType as InterfaceType; | |
610 if (constructor.isFactory) { | |
611 // We couldn't find a non-factory constructor. | |
612 // See if it's because we reached an external const factory constructor | |
613 // that we can emulate. | |
614 if (constructor.name == "fromEnvironment") { | |
615 if (!checkFromEnvironmentArguments( | |
616 arguments, argumentValues, namedArgumentValues, definingClass)) { | |
617 errorReporter.reportErrorForNode( | |
618 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node); | |
619 return null; | |
620 } | |
621 String variableName = | |
622 argumentCount < 1 ? null : argumentValues[0].stringValue; | |
623 if (identical(definingClass, typeProvider.boolType)) { | |
624 DartObject valueFromEnvironment; | |
625 valueFromEnvironment = | |
626 _declaredVariables.getBool(typeProvider, variableName); | |
627 return computeValueFromEnvironment(valueFromEnvironment, | |
628 new DartObjectImpl(typeProvider.boolType, BoolState.FALSE_STATE), | |
629 namedArgumentValues); | |
630 } else if (identical(definingClass, typeProvider.intType)) { | |
631 DartObject valueFromEnvironment; | |
632 valueFromEnvironment = | |
633 _declaredVariables.getInt(typeProvider, variableName); | |
634 return computeValueFromEnvironment(valueFromEnvironment, | |
635 new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE), | |
636 namedArgumentValues); | |
637 } else if (identical(definingClass, typeProvider.stringType)) { | |
638 DartObject valueFromEnvironment; | |
639 valueFromEnvironment = | |
640 _declaredVariables.getString(typeProvider, variableName); | |
641 return computeValueFromEnvironment(valueFromEnvironment, | |
642 new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE), | |
643 namedArgumentValues); | |
644 } | |
645 } else if (constructor.name == "" && | |
646 identical(definingClass, typeProvider.symbolType) && | |
647 argumentCount == 1) { | |
648 if (!checkSymbolArguments( | |
649 arguments, argumentValues, namedArgumentValues)) { | |
650 errorReporter.reportErrorForNode( | |
651 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node); | |
652 return null; | |
653 } | |
654 String argumentValue = argumentValues[0].stringValue; | |
655 return new DartObjectImpl( | |
656 definingClass, new SymbolState(argumentValue)); | |
657 } | |
658 // Either it's an external const factory constructor that we can't | |
659 // emulate, or an error occurred (a cycle, or a const constructor trying | |
660 // to delegate to a non-const constructor). | |
661 // In the former case, the best we can do is consider it an unknown value. | |
662 // In the latter case, the error has already been reported, so considering | |
663 // it an unknown value will suppress further errors. | |
664 return new DartObjectImpl.validWithUnknownValue(definingClass); | |
665 } | |
666 ConstructorElementImpl constructorBase = _getConstructorBase(constructor); | |
667 validator.beforeGetConstantInitializers(constructorBase); | |
668 List<ConstructorInitializer> initializers = | |
669 constructorBase.constantInitializers; | |
670 if (initializers == null) { | |
671 // This can happen in some cases where there are compile errors in the | |
672 // code being analyzed (for example if the code is trying to create a | |
673 // const instance using a non-const constructor, or the node we're | |
674 // visiting is involved in a cycle). The error has already been reported, | |
675 // so consider it an unknown value to suppress further errors. | |
676 return new DartObjectImpl.validWithUnknownValue(definingClass); | |
677 } | |
678 HashMap<String, DartObjectImpl> fieldMap = | |
679 new HashMap<String, DartObjectImpl>(); | |
680 // Start with final fields that are initialized at their declaration site. | |
681 for (FieldElement field in constructor.enclosingElement.fields) { | |
682 if ((field.isFinal || field.isConst) && | |
683 !field.isStatic && | |
684 field is ConstFieldElementImpl) { | |
685 validator.beforeGetFieldEvaluationResult(field); | |
686 EvaluationResultImpl evaluationResult = field.evaluationResult; | |
687 DartType fieldType = | |
688 FieldMember.from(field, constructor.returnType).type; | |
689 DartObjectImpl fieldValue = evaluationResult.value; | |
690 if (fieldValue != null && !runtimeTypeMatch(fieldValue, fieldType)) { | |
691 errorReporter.reportErrorForNode( | |
692 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMA
TCH, | |
693 node, [fieldValue.type, field.name, fieldType]); | |
694 } | |
695 fieldMap[field.name] = evaluationResult.value; | |
696 } | |
697 } | |
698 // Now evaluate the constructor declaration. | |
699 HashMap<String, DartObjectImpl> parameterMap = | |
700 new HashMap<String, DartObjectImpl>(); | |
701 List<ParameterElement> parameters = constructor.parameters; | |
702 int parameterCount = parameters.length; | |
703 for (int i = 0; i < parameterCount; i++) { | |
704 ParameterElement parameter = parameters[i]; | |
705 ParameterElement baseParameter = parameter; | |
706 while (baseParameter is ParameterMember) { | |
707 baseParameter = (baseParameter as ParameterMember).baseElement; | |
708 } | |
709 DartObjectImpl argumentValue = null; | |
710 AstNode errorTarget = null; | |
711 if (baseParameter.parameterKind == ParameterKind.NAMED) { | |
712 argumentValue = namedArgumentValues[baseParameter.name]; | |
713 errorTarget = namedArgumentNodes[baseParameter.name]; | |
714 } else if (i < argumentCount) { | |
715 argumentValue = argumentValues[i]; | |
716 errorTarget = argumentNodes[i]; | |
717 } | |
718 if (errorTarget == null) { | |
719 // No argument node that we can direct error messages to, because we | |
720 // are handling an optional parameter that wasn't specified. So just | |
721 // direct error messages to the constructor call. | |
722 errorTarget = node; | |
723 } | |
724 if (argumentValue == null && baseParameter is ParameterElementImpl) { | |
725 // The parameter is an optional positional parameter for which no value | |
726 // was provided, so use the default value. | |
727 validator.beforeGetParameterDefault(baseParameter); | |
728 EvaluationResultImpl evaluationResult = baseParameter.evaluationResult; | |
729 if (evaluationResult == null) { | |
730 // No default was provided, so the default value is null. | |
731 argumentValue = typeProvider.nullObject; | |
732 } else if (evaluationResult.value != null) { | |
733 argumentValue = evaluationResult.value; | |
734 } | |
735 } | |
736 if (argumentValue != null) { | |
737 if (!runtimeTypeMatch(argumentValue, parameter.type)) { | |
738 errorReporter.reportErrorForNode( | |
739 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMA
TCH, | |
740 errorTarget, [argumentValue.type, parameter.type]); | |
741 } | |
742 if (baseParameter.isInitializingFormal) { | |
743 FieldElement field = (parameter as FieldFormalParameterElement).field; | |
744 if (field != null) { | |
745 DartType fieldType = field.type; | |
746 if (fieldType != parameter.type) { | |
747 // We've already checked that the argument can be assigned to the | |
748 // parameter; we also need to check that it can be assigned to | |
749 // the field. | |
750 if (!runtimeTypeMatch(argumentValue, fieldType)) { | |
751 errorReporter.reportErrorForNode( | |
752 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE
_MISMATCH, | |
753 errorTarget, [argumentValue.type, fieldType]); | |
754 } | |
755 } | |
756 String fieldName = field.name; | |
757 if (fieldMap.containsKey(fieldName)) { | |
758 errorReporter.reportErrorForNode( | |
759 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node); | |
760 } | |
761 fieldMap[fieldName] = argumentValue; | |
762 } | |
763 } else { | |
764 String name = baseParameter.name; | |
765 parameterMap[name] = argumentValue; | |
766 } | |
767 } | |
768 } | |
769 ConstantVisitor initializerVisitor = new ConstantVisitor( | |
770 this, errorReporter, lexicalEnvironment: parameterMap); | |
771 String superName = null; | |
772 NodeList<Expression> superArguments = null; | |
773 for (ConstructorInitializer initializer in initializers) { | |
774 if (initializer is ConstructorFieldInitializer) { | |
775 ConstructorFieldInitializer constructorFieldInitializer = initializer; | |
776 Expression initializerExpression = | |
777 constructorFieldInitializer.expression; | |
778 DartObjectImpl evaluationResult = | |
779 initializerExpression.accept(initializerVisitor); | |
780 if (evaluationResult != null) { | |
781 String fieldName = constructorFieldInitializer.fieldName.name; | |
782 if (fieldMap.containsKey(fieldName)) { | |
783 errorReporter.reportErrorForNode( | |
784 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, node); | |
785 } | |
786 fieldMap[fieldName] = evaluationResult; | |
787 PropertyAccessorElement getter = definingClass.getGetter(fieldName); | |
788 if (getter != null) { | |
789 PropertyInducingElement field = getter.variable; | |
790 if (!runtimeTypeMatch(evaluationResult, field.type)) { | |
791 errorReporter.reportErrorForNode( | |
792 CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_M
ISMATCH, | |
793 node, [evaluationResult.type, fieldName, field.type]); | |
794 } | |
795 } | |
796 } | |
797 } else if (initializer is SuperConstructorInvocation) { | |
798 SuperConstructorInvocation superConstructorInvocation = initializer; | |
799 SimpleIdentifier name = superConstructorInvocation.constructorName; | |
800 if (name != null) { | |
801 superName = name.name; | |
802 } | |
803 superArguments = superConstructorInvocation.argumentList.arguments; | |
804 } else if (initializer is RedirectingConstructorInvocation) { | |
805 // This is a redirecting constructor, so just evaluate the constructor | |
806 // it redirects to. | |
807 ConstructorElement constructor = initializer.staticElement; | |
808 if (constructor != null && constructor.isConst) { | |
809 return evaluateConstructorCall(node, | |
810 initializer.argumentList.arguments, constructor, | |
811 initializerVisitor, errorReporter); | |
812 } | |
813 } | |
814 } | |
815 // Evaluate explicit or implicit call to super(). | |
816 InterfaceType superclass = definingClass.superclass; | |
817 if (superclass != null && !superclass.isObject) { | |
818 ConstructorElement superConstructor = | |
819 superclass.lookUpConstructor(superName, constructor.library); | |
820 if (superConstructor != null) { | |
821 if (superArguments == null) { | |
822 superArguments = new NodeList<Expression>(null); | |
823 } | |
824 evaluateSuperConstructorCall(node, fieldMap, superConstructor, | |
825 superArguments, initializerVisitor, errorReporter); | |
826 } | |
827 } | |
828 return new DartObjectImpl(definingClass, new GenericState(fieldMap)); | |
829 } | |
830 | |
831 void evaluateSuperConstructorCall(AstNode node, | |
832 HashMap<String, DartObjectImpl> fieldMap, | |
833 ConstructorElement superConstructor, NodeList<Expression> superArguments, | |
834 ConstantVisitor initializerVisitor, ErrorReporter errorReporter) { | |
835 if (superConstructor != null && superConstructor.isConst) { | |
836 DartObjectImpl evaluationResult = evaluateConstructorCall(node, | |
837 superArguments, superConstructor, initializerVisitor, errorReporter); | |
838 if (evaluationResult != null) { | |
839 fieldMap[GenericState.SUPERCLASS_FIELD] = evaluationResult; | |
840 } | |
841 } | |
842 } | |
843 | |
844 /** | |
845 * Attempt to follow the chain of factory redirections until a constructor is | |
846 * reached which is not a const factory constructor. Return the constant | |
847 * constructor which terminates the chain of factory redirections, if the | |
848 * chain terminates. If there is a problem (e.g. a redirection can't be found, | |
849 * or a cycle is encountered), the chain will be followed as far as possible | |
850 * and then a const factory constructor will be returned. | |
851 */ | |
852 ConstructorElement followConstantRedirectionChain( | |
853 ConstructorElement constructor) { | |
854 HashSet<ConstructorElement> constructorsVisited = | |
855 new HashSet<ConstructorElement>(); | |
856 while (true) { | |
857 ConstructorElement redirectedConstructor = | |
858 getConstRedirectedConstructor(constructor); | |
859 if (redirectedConstructor == null) { | |
860 break; | |
861 } else { | |
862 ConstructorElement constructorBase = _getConstructorBase(constructor); | |
863 constructorsVisited.add(constructorBase); | |
864 ConstructorElement redirectedConstructorBase = | |
865 _getConstructorBase(redirectedConstructor); | |
866 if (constructorsVisited.contains(redirectedConstructorBase)) { | |
867 // Cycle in redirecting factory constructors--this is not allowed | |
868 // and is checked elsewhere--see | |
869 // [ErrorVerifier.checkForRecursiveFactoryRedirect()]). | |
870 break; | |
871 } | |
872 } | |
873 constructor = redirectedConstructor; | |
874 } | |
875 return constructor; | |
876 } | |
877 | |
878 /** | |
879 * Generate an error indicating that the given [constant] is not a valid | |
880 * compile-time constant because it references at least one of the constants | |
881 * in the given [cycle], each of which directly or indirectly references the | |
882 * constant. | |
883 */ | |
884 void generateCycleError(Iterable<ConstantEvaluationTarget> cycle, | |
885 ConstantEvaluationTarget constant) { | |
886 if (constant is VariableElement) { | |
887 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
888 ErrorReporter errorReporter = | |
889 new ErrorReporter(errorListener, constant.source); | |
890 // TODO(paulberry): It would be really nice if we could extract enough | |
891 // information from the 'cycle' argument to provide the user with a | |
892 // description of the cycle. | |
893 errorReporter.reportErrorForElement( | |
894 CompileTimeErrorCode.RECURSIVE_COMPILE_TIME_CONSTANT, constant, []); | |
895 (constant as VariableElementImpl).evaluationResult = | |
896 new EvaluationResultImpl(null, errorListener.errors); | |
897 } else if (constant is ConstructorElement) { | |
898 // We don't report cycle errors on constructor declarations since there | |
899 // is nowhere to put the error information. | |
900 } else { | |
901 // Should not happen. Formal parameter defaults and annotations should | |
902 // never appear as part of a cycle because they can't be referred to. | |
903 assert(false); | |
904 AnalysisEngine.instance.logger.logError( | |
905 "Constant value computer trying to report a cycle error for a node of
type ${constant.runtimeType}"); | |
906 } | |
907 } | |
908 | |
909 /** | |
910 * If [constructor] redirects to another const constructor, return the | |
911 * const constructor it redirects to. Otherwise return `null`. | |
912 */ | |
913 ConstructorElement getConstRedirectedConstructor( | |
914 ConstructorElement constructor) { | |
915 if (!constructor.isFactory) { | |
916 return null; | |
917 } | |
918 if (identical(constructor.enclosingElement.type, typeProvider.symbolType)) { | |
919 // The dart:core.Symbol has a const factory constructor that redirects | |
920 // to dart:_internal.Symbol. That in turn redirects to an external | |
921 // const constructor, which we won't be able to evaluate. | |
922 // So stop following the chain of redirections at dart:core.Symbol, and | |
923 // let [evaluateInstanceCreationExpression] handle it specially. | |
924 return null; | |
925 } | |
926 ConstructorElement redirectedConstructor = | |
927 constructor.redirectedConstructor; | |
928 if (redirectedConstructor == null) { | |
929 // This can happen if constructor is an external factory constructor. | |
930 return null; | |
931 } | |
932 if (!redirectedConstructor.isConst) { | |
933 // Delegating to a non-const constructor--this is not allowed (and | |
934 // is checked elsewhere--see | |
935 // [ErrorVerifier.checkForRedirectToNonConstConstructor()]). | |
936 return null; | |
937 } | |
938 return redirectedConstructor; | |
939 } | |
940 | |
941 /** | |
942 * Check if the object [obj] matches the type [type] according to runtime type | |
943 * checking rules. | |
944 */ | |
945 bool runtimeTypeMatch(DartObjectImpl obj, DartType type) { | |
946 if (obj.isNull) { | |
947 return true; | |
948 } | |
949 if (type.isUndefined) { | |
950 return false; | |
951 } | |
952 return obj.type.isSubtypeOf(type); | |
953 } | |
954 | |
955 /** | |
956 * Determine whether the given string is a valid name for a public symbol | |
957 * (i.e. whether it is allowed for a call to the Symbol constructor). | |
958 */ | |
959 static bool isValidPublicSymbol(String name) => name.isEmpty || | |
960 name == "void" || | |
961 new JavaPatternMatcher(_PUBLIC_SYMBOL_PATTERN, name).matches(); | |
962 | |
963 static ConstructorElementImpl _getConstructorBase( | |
964 ConstructorElement constructor) { | |
965 while (constructor is ConstructorMember) { | |
966 constructor = (constructor as ConstructorMember).baseElement; | |
967 } | |
968 return constructor; | |
969 } | |
970 } | |
971 | |
972 /** | |
973 * Wrapper around an [Annotation] which can be used as a | |
974 * [ConstantEvaluationTarget]. | |
975 */ | |
976 class ConstantEvaluationTarget_Annotation implements ConstantEvaluationTarget { | |
977 final AnalysisContext context; | |
978 final Source source; | |
979 final Source librarySource; | |
980 final Annotation annotation; | |
981 | |
982 ConstantEvaluationTarget_Annotation( | |
983 this.context, this.source, this.librarySource, this.annotation); | |
984 | |
985 @override | |
986 int get hashCode => JenkinsSmiHash.hash3( | |
987 source.hashCode, librarySource.hashCode, annotation.hashCode); | |
988 | |
989 @override | |
990 bool operator ==(other) { | |
991 if (other is ConstantEvaluationTarget_Annotation) { | |
992 return this.context == other.context && | |
993 this.source == other.source && | |
994 this.librarySource == other.librarySource && | |
995 this.annotation == other.annotation; | |
996 } else { | |
997 return false; | |
998 } | |
999 } | |
1000 } | |
1001 | |
1002 /** | |
1003 * Interface used by unit tests to verify correct dependency analysis during | |
1004 * constant evaluation. | |
1005 */ | |
1006 abstract class ConstantEvaluationValidator { | |
1007 /** | |
1008 * This method is called just before computing the constant value associated | |
1009 * with [constant]. Unit tests will override this method to introduce | |
1010 * additional error checking. | |
1011 */ | |
1012 void beforeComputeValue(ConstantEvaluationTarget constant); | |
1013 | |
1014 /** | |
1015 * This method is called just before getting the constant initializers | |
1016 * associated with the [constructor]. Unit tests will override this method to | |
1017 * introduce additional error checking. | |
1018 */ | |
1019 void beforeGetConstantInitializers(ConstructorElement constructor); | |
1020 | |
1021 /** | |
1022 * This method is called just before retrieving an evaluation result from an | |
1023 * element. Unit tests will override it to introduce additional error | |
1024 * checking. | |
1025 */ | |
1026 void beforeGetEvaluationResult(ConstantEvaluationTarget constant); | |
1027 | |
1028 /** | |
1029 * This method is called just before getting the constant value of a field | |
1030 * with an initializer. Unit tests will override this method to introduce | |
1031 * additional error checking. | |
1032 */ | |
1033 void beforeGetFieldEvaluationResult(FieldElementImpl field); | |
1034 | |
1035 /** | |
1036 * This method is called just before getting a parameter's default value. Unit | |
1037 * tests will override this method to introduce additional error checking. | |
1038 */ | |
1039 void beforeGetParameterDefault(ParameterElement parameter); | |
1040 } | |
1041 | |
1042 /** | |
1043 * Implementation of [ConstantEvaluationValidator] used in production; does no | |
1044 * validation. | |
1045 */ | |
1046 class ConstantEvaluationValidator_ForProduction | |
1047 implements ConstantEvaluationValidator { | |
1048 @override | |
1049 void beforeComputeValue(ConstantEvaluationTarget constant) {} | |
1050 | |
1051 @override | |
1052 void beforeGetConstantInitializers(ConstructorElement constructor) {} | |
1053 | |
1054 @override | |
1055 void beforeGetEvaluationResult(ConstantEvaluationTarget constant) {} | |
1056 | |
1057 @override | |
1058 void beforeGetFieldEvaluationResult(FieldElementImpl field) {} | |
1059 | |
1060 @override | |
1061 void beforeGetParameterDefault(ParameterElement parameter) {} | |
1062 } | |
1063 | |
1064 /** | |
1065 * Instances of the class `ConstantEvaluator` evaluate constant expressions to | |
1066 * produce their compile-time value. According to the Dart Language | |
1067 * Specification: | |
1068 * <blockquote> | |
1069 * A constant expression is one of the following: | |
1070 * * A literal number. | |
1071 * * A literal boolean. | |
1072 * * A literal string where any interpolated expression is a compile-time | |
1073 * constant that evaluates to a numeric, string or boolean value or to | |
1074 * <b>null</b>. | |
1075 * * A literal symbol. | |
1076 * * <b>null</b>. | |
1077 * * A qualified reference to a static constant variable. | |
1078 * * An identifier expression that denotes a constant variable, class or type | |
1079 * alias. | |
1080 * * A constant constructor invocation. | |
1081 * * A constant list literal. | |
1082 * * A constant map literal. | |
1083 * * A simple or qualified identifier denoting a top-level function or a static | |
1084 * method. | |
1085 * * A parenthesized expression <i>(e)</i> where <i>e</i> is a constant | |
1086 * expression. | |
1087 * * An expression of the form <i>identical(e<sub>1</sub>, e<sub>2</sub>)</i> | |
1088 * where <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant | |
1089 * expressions and <i>identical()</i> is statically bound to the predefined | |
1090 * dart function <i>identical()</i> discussed above. | |
1091 * * An expression of one of the forms <i>e<sub>1</sub> == e<sub>2</sub></i> or | |
1092 * <i>e<sub>1</sub> != e<sub>2</sub></i> where <i>e<sub>1</sub></i> and | |
1093 * <i>e<sub>2</sub></i> are constant expressions that evaluate to a numeric, | |
1094 * string or boolean value. | |
1095 * * An expression of one of the forms <i>!e</i>, <i>e<sub>1</sub> && | |
1096 * e<sub>2</sub></i> or <i>e<sub>1</sub> || e<sub>2</sub></i>, where <i>e</i>, | |
1097 * <i>e1</sub></i> and <i>e2</sub></i> are constant expressions that evaluate | |
1098 * to a boolean value. | |
1099 * * An expression of one of the forms <i>~e</i>, <i>e<sub>1</sub> ^ | |
1100 * e<sub>2</sub></i>, <i>e<sub>1</sub> & e<sub>2</sub></i>, | |
1101 * <i>e<sub>1</sub> | e<sub>2</sub></i>, <i>e<sub>1</sub> >> | |
1102 * e<sub>2</sub></i> or <i>e<sub>1</sub> << e<sub>2</sub></i>, where | |
1103 * <i>e</i>, <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant | |
1104 * expressions that evaluate to an integer value or to <b>null</b>. | |
1105 * * An expression of one of the forms <i>-e</i>, <i>e<sub>1</sub> + | |
1106 * e<sub>2</sub></i>, <i>e<sub>1</sub> -e<sub>2</sub></i>, <i>e<sub>1</sub> * | |
1107 * e<sub>2</sub></i>, <i>e<sub>1</sub> / e<sub>2</sub></i>, <i>e<sub>1</sub> | |
1108 * ~/ e<sub>2</sub></i>, <i>e<sub>1</sub> > e<sub>2</sub></i>, | |
1109 * <i>e<sub>1</sub> < e<sub>2</sub></i>, <i>e<sub>1</sub> >= | |
1110 * e<sub>2</sub></i>, <i>e<sub>1</sub> <= e<sub>2</sub></i> or | |
1111 * <i>e<sub>1</sub> % e<sub>2</sub></i>, where <i>e</i>, <i>e<sub>1</sub></i> | |
1112 * and <i>e<sub>2</sub></i> are constant expressions that evaluate to a | |
1113 * numeric value or to <b>null</b>. | |
1114 * * An expression of the form <i>e<sub>1</sub> ? e<sub>2</sub> : | |
1115 * e<sub>3</sub></i> where <i>e<sub>1</sub></i>, <i>e<sub>2</sub></i> and | |
1116 * <i>e<sub>3</sub></i> are constant expressions, and <i>e<sub>1</sub></i> | |
1117 * evaluates to a boolean value. | |
1118 * </blockquote> | |
1119 */ | |
1120 class ConstantEvaluator { | |
1121 /** | |
1122 * The source containing the expression(s) that will be evaluated. | |
1123 */ | |
1124 final Source _source; | |
1125 | |
1126 /** | |
1127 * The type provider used to access the known types. | |
1128 */ | |
1129 final TypeProvider _typeProvider; | |
1130 | |
1131 /** | |
1132 * Initialize a newly created evaluator to evaluate expressions in the given | |
1133 * [source]. The [typeProvider] is the type provider used to access known | |
1134 * types. | |
1135 */ | |
1136 ConstantEvaluator(this._source, this._typeProvider); | |
1137 | |
1138 EvaluationResult evaluate(Expression expression) { | |
1139 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
1140 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source); | |
1141 DartObjectImpl result = expression.accept(new ConstantVisitor( | |
1142 new ConstantEvaluationEngine(_typeProvider, new DeclaredVariables()), | |
1143 errorReporter)); | |
1144 if (result != null) { | |
1145 return EvaluationResult.forValue(result); | |
1146 } | |
1147 return EvaluationResult.forErrors(errorListener.errors); | |
1148 } | |
1149 } | |
1150 | |
1151 /** | |
1152 * A visitor used to traverse the AST structures of all of the compilation units | |
1153 * being resolved and build tables of the constant variables, constant | |
1154 * constructors, constant constructor invocations, and annotations found in | |
1155 * those compilation units. | |
1156 */ | |
1157 class ConstantFinder extends RecursiveAstVisitor<Object> { | |
1158 final AnalysisContext context; | |
1159 final Source source; | |
1160 final Source librarySource; | |
1161 | |
1162 /** | |
1163 * The elements and AST nodes whose constant values need to be computed. | |
1164 */ | |
1165 HashSet<ConstantEvaluationTarget> constantsToCompute = | |
1166 new HashSet<ConstantEvaluationTarget>(); | |
1167 | |
1168 /** | |
1169 * True if instance variables marked as "final" should be treated as "const". | |
1170 */ | |
1171 bool treatFinalInstanceVarAsConst = false; | |
1172 | |
1173 ConstantFinder(this.context, this.source, this.librarySource); | |
1174 | |
1175 @override | |
1176 Object visitAnnotation(Annotation node) { | |
1177 super.visitAnnotation(node); | |
1178 constantsToCompute.add(new ConstantEvaluationTarget_Annotation( | |
1179 context, source, librarySource, node)); | |
1180 return null; | |
1181 } | |
1182 | |
1183 @override | |
1184 Object visitClassDeclaration(ClassDeclaration node) { | |
1185 bool prevTreatFinalInstanceVarAsConst = treatFinalInstanceVarAsConst; | |
1186 if (node.element.constructors.any((ConstructorElement e) => e.isConst)) { | |
1187 // Instance vars marked "final" need to be included in the dependency | |
1188 // graph, since constant constructors implicitly use the values in their | |
1189 // initializers. | |
1190 treatFinalInstanceVarAsConst = true; | |
1191 } | |
1192 try { | |
1193 return super.visitClassDeclaration(node); | |
1194 } finally { | |
1195 treatFinalInstanceVarAsConst = prevTreatFinalInstanceVarAsConst; | |
1196 } | |
1197 } | |
1198 | |
1199 @override | |
1200 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
1201 super.visitConstructorDeclaration(node); | |
1202 if (node.constKeyword != null) { | |
1203 ConstructorElement element = node.element; | |
1204 if (element != null) { | |
1205 constantsToCompute.add(element); | |
1206 constantsToCompute.addAll(element.parameters); | |
1207 } | |
1208 } | |
1209 return null; | |
1210 } | |
1211 | |
1212 @override | |
1213 Object visitVariableDeclaration(VariableDeclaration node) { | |
1214 super.visitVariableDeclaration(node); | |
1215 Expression initializer = node.initializer; | |
1216 VariableElement element = node.element; | |
1217 if (initializer != null && | |
1218 (node.isConst || | |
1219 treatFinalInstanceVarAsConst && | |
1220 element is FieldElement && | |
1221 node.isFinal && | |
1222 !element.isStatic)) { | |
1223 if (node.element != null) { | |
1224 constantsToCompute.add(node.element); | |
1225 } | |
1226 } | |
1227 return null; | |
1228 } | |
1229 } | |
1230 | |
1231 /** | |
1232 * An object used to compute the values of constant variables and constant | |
1233 * constructor invocations in one or more compilation units. The expected usage | |
1234 * pattern is for the compilation units to be added to this computer using the | |
1235 * method [add] and then for the method [computeValues] to be invoked exactly | |
1236 * once. Any use of an instance after invoking the method [computeValues] will | |
1237 * result in unpredictable behavior. | |
1238 */ | |
1239 class ConstantValueComputer { | |
1240 /** | |
1241 * Source of RegExp matching declarable operator names. | |
1242 * From sdk/lib/internal/symbol.dart. | |
1243 */ | |
1244 static String _OPERATOR_RE = | |
1245 "(?:[\\-+*/%&|^]|\\[\\]=?|==|~/?|<[<=]?|>[>=]?|unary-)"; | |
1246 | |
1247 /** | |
1248 * Source of RegExp matching Dart reserved words. | |
1249 * From sdk/lib/internal/symbol.dart. | |
1250 */ | |
1251 static String _RESERVED_WORD_RE = | |
1252 "(?: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))"; | |
1253 | |
1254 /** | |
1255 * A graph in which the nodes are the constants, and the edges are from each | |
1256 * constant to the other constants that are referenced by it. | |
1257 */ | |
1258 DirectedGraph<ConstantEvaluationTarget> referenceGraph = | |
1259 new DirectedGraph<ConstantEvaluationTarget>(); | |
1260 | |
1261 /** | |
1262 * The elements whose constant values need to be computed. Any elements | |
1263 * which appear in [referenceGraph] but not in this set either belong to a | |
1264 * different library cycle (and hence don't need to be recomputed) or were | |
1265 * computed during a previous stage of resolution stage (e.g. constants | |
1266 * associated with enums). | |
1267 */ | |
1268 HashSet<ConstantEvaluationTarget> _constantsToCompute = | |
1269 new HashSet<ConstantEvaluationTarget>(); | |
1270 | |
1271 /** | |
1272 * The evaluation engine that does the work of evaluating instance creation | |
1273 * expressions. | |
1274 */ | |
1275 final ConstantEvaluationEngine evaluationEngine; | |
1276 | |
1277 final AnalysisContext _context; | |
1278 | |
1279 /** | |
1280 * Initialize a newly created constant value computer. The [typeProvider] is | |
1281 * the type provider used to access known types. The [declaredVariables] is | |
1282 * the set of variables declared on the command line using '-D'. | |
1283 */ | |
1284 ConstantValueComputer(this._context, TypeProvider typeProvider, | |
1285 DeclaredVariables declaredVariables, | |
1286 [ConstantEvaluationValidator validator]) | |
1287 : evaluationEngine = new ConstantEvaluationEngine( | |
1288 typeProvider, declaredVariables, validator: validator); | |
1289 | |
1290 /** | |
1291 * Add the constants in the given compilation [unit] to the list of constants | |
1292 * whose value needs to be computed. | |
1293 */ | |
1294 void add(CompilationUnit unit, Source source, Source librarySource) { | |
1295 ConstantFinder constantFinder = | |
1296 new ConstantFinder(_context, source, librarySource); | |
1297 unit.accept(constantFinder); | |
1298 _constantsToCompute.addAll(constantFinder.constantsToCompute); | |
1299 } | |
1300 | |
1301 /** | |
1302 * Compute values for all of the constants in the compilation units that were | |
1303 * added. | |
1304 */ | |
1305 void computeValues() { | |
1306 for (ConstantEvaluationTarget constant in _constantsToCompute) { | |
1307 referenceGraph.addNode(constant); | |
1308 evaluationEngine.computeDependencies(constant, | |
1309 (ConstantEvaluationTarget dependency) { | |
1310 referenceGraph.addEdge(constant, dependency); | |
1311 }); | |
1312 } | |
1313 List<List<ConstantEvaluationTarget>> topologicalSort = | |
1314 referenceGraph.computeTopologicalSort(); | |
1315 for (List<ConstantEvaluationTarget> constantsInCycle in topologicalSort) { | |
1316 if (constantsInCycle.length == 1) { | |
1317 ConstantEvaluationTarget constant = constantsInCycle[0]; | |
1318 if (!referenceGraph.getTails(constant).contains(constant)) { | |
1319 _computeValueFor(constant); | |
1320 continue; | |
1321 } | |
1322 } | |
1323 for (ConstantEvaluationTarget constant in constantsInCycle) { | |
1324 evaluationEngine.generateCycleError(constantsInCycle, constant); | |
1325 } | |
1326 } | |
1327 } | |
1328 | |
1329 /** | |
1330 * Compute a value for the given [constant]. | |
1331 */ | |
1332 void _computeValueFor(ConstantEvaluationTarget constant) { | |
1333 if (!_constantsToCompute.contains(constant)) { | |
1334 // Element is in the dependency graph but should have been computed by | |
1335 // a previous stage of analysis. | |
1336 // TODO(paulberry): once we have moved over to the new task model, this | |
1337 // should only occur for constants associated with enum members. Once | |
1338 // that happens we should add an assertion to verify that it doesn't | |
1339 // occur in any other cases. | |
1340 return; | |
1341 } | |
1342 evaluationEngine.computeConstantValue(constant); | |
1343 } | |
1344 } | |
1345 | |
1346 /** | |
1347 * A visitor used to evaluate constant expressions to produce their compile-time | |
1348 * value. According to the Dart Language Specification: <blockquote> A constant | |
1349 * expression is one of the following: | |
1350 * | |
1351 * * A literal number. | |
1352 * * A literal boolean. | |
1353 * * A literal string where any interpolated expression is a compile-time | |
1354 * constant that evaluates to a numeric, string or boolean value or to | |
1355 * <b>null</b>. | |
1356 * * A literal symbol. | |
1357 * * <b>null</b>. | |
1358 * * A qualified reference to a static constant variable. | |
1359 * * An identifier expression that denotes a constant variable, class or type | |
1360 * alias. | |
1361 * * A constant constructor invocation. | |
1362 * * A constant list literal. | |
1363 * * A constant map literal. | |
1364 * * A simple or qualified identifier denoting a top-level function or a static | |
1365 * method. | |
1366 * * A parenthesized expression <i>(e)</i> where <i>e</i> is a constant | |
1367 * expression. | |
1368 * * An expression of the form <i>identical(e<sub>1</sub>, e<sub>2</sub>)</i> | |
1369 * where <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant | |
1370 * expressions and <i>identical()</i> is statically bound to the predefined | |
1371 * dart function <i>identical()</i> discussed above. | |
1372 * * An expression of one of the forms <i>e<sub>1</sub> == e<sub>2</sub></i> or | |
1373 * <i>e<sub>1</sub> != e<sub>2</sub></i> where <i>e<sub>1</sub></i> and | |
1374 * <i>e<sub>2</sub></i> are constant expressions that evaluate to a numeric, | |
1375 * string or boolean value. | |
1376 * * An expression of one of the forms <i>!e</i>, <i>e<sub>1</sub> && | |
1377 * e<sub>2</sub></i> or <i>e<sub>1</sub> || e<sub>2</sub></i>, where <i>e</i>, | |
1378 * <i>e1</sub></i> and <i>e2</sub></i> are constant expressions that evaluate | |
1379 * to a boolean value. | |
1380 * * An expression of one of the forms <i>~e</i>, <i>e<sub>1</sub> ^ | |
1381 * e<sub>2</sub></i>, <i>e<sub>1</sub> & e<sub>2</sub></i>, | |
1382 * <i>e<sub>1</sub> | e<sub>2</sub></i>, <i>e<sub>1</sub> >> | |
1383 * e<sub>2</sub></i> or <i>e<sub>1</sub> << e<sub>2</sub></i>, where | |
1384 * <i>e</i>, <i>e<sub>1</sub></i> and <i>e<sub>2</sub></i> are constant | |
1385 * expressions that evaluate to an integer value or to <b>null</b>. | |
1386 * * An expression of one of the forms <i>-e</i>, <i>e<sub>1</sub> + | |
1387 * e<sub>2</sub></i>, <i>e<sub>1</sub> - e<sub>2</sub></i>, <i>e<sub>1</sub> * | |
1388 * e<sub>2</sub></i>, <i>e<sub>1</sub> / e<sub>2</sub></i>, <i>e<sub>1</sub> | |
1389 * ~/ e<sub>2</sub></i>, <i>e<sub>1</sub> > e<sub>2</sub></i>, | |
1390 * <i>e<sub>1</sub> < e<sub>2</sub></i>, <i>e<sub>1</sub> >= | |
1391 * e<sub>2</sub></i>, <i>e<sub>1</sub> <= e<sub>2</sub></i> or | |
1392 * <i>e<sub>1</sub> % e<sub>2</sub></i>, where <i>e</i>, <i>e<sub>1</sub></i> | |
1393 * and <i>e<sub>2</sub></i> are constant expressions that evaluate to a | |
1394 * numeric value or to <b>null</b>. | |
1395 * * An expression of the form <i>e<sub>1</sub> ? e<sub>2</sub> : | |
1396 * e<sub>3</sub></i> where <i>e<sub>1</sub></i>, <i>e<sub>2</sub></i> and | |
1397 * <i>e<sub>3</sub></i> are constant expressions, and <i>e<sub>1</sub></i> | |
1398 * evaluates to a boolean value. | |
1399 * </blockquote> | |
1400 */ | |
1401 class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> { | |
1402 /** | |
1403 * The type provider used to access the known types. | |
1404 */ | |
1405 final ConstantEvaluationEngine evaluationEngine; | |
1406 | |
1407 final HashMap<String, DartObjectImpl> _lexicalEnvironment; | |
1408 | |
1409 /** | |
1410 * Error reporter that we use to report errors accumulated while computing the | |
1411 * constant. | |
1412 */ | |
1413 final ErrorReporter _errorReporter; | |
1414 | |
1415 /** | |
1416 * Helper class used to compute constant values. | |
1417 */ | |
1418 DartObjectComputer _dartObjectComputer; | |
1419 | |
1420 /** | |
1421 * Initialize a newly created constant visitor. The [evaluationEngine] is | |
1422 * used to evaluate instance creation expressions. The [lexicalEnvironment] | |
1423 * is a map containing values which should override identifiers, or `null` if | |
1424 * no overriding is necessary. The [_errorReporter] is used to report errors | |
1425 * found during evaluation. The [validator] is used by unit tests to verify | |
1426 * correct dependency analysis. | |
1427 */ | |
1428 ConstantVisitor(this.evaluationEngine, this._errorReporter, | |
1429 {HashMap<String, DartObjectImpl> lexicalEnvironment}) | |
1430 : _lexicalEnvironment = lexicalEnvironment { | |
1431 this._dartObjectComputer = | |
1432 new DartObjectComputer(_errorReporter, evaluationEngine.typeProvider); | |
1433 } | |
1434 | |
1435 /** | |
1436 * Convenience getter to gain access to the [evalationEngine]'s type | |
1437 * provider. | |
1438 */ | |
1439 TypeProvider get _typeProvider => evaluationEngine.typeProvider; | |
1440 | |
1441 /** | |
1442 * Convenience getter to gain access to the [evaluationEngine]'s type system. | |
1443 */ | |
1444 TypeSystem get _typeSystem => evaluationEngine.typeSystem; | |
1445 | |
1446 @override | |
1447 DartObjectImpl visitAdjacentStrings(AdjacentStrings node) { | |
1448 DartObjectImpl result = null; | |
1449 for (StringLiteral string in node.strings) { | |
1450 if (result == null) { | |
1451 result = string.accept(this); | |
1452 } else { | |
1453 result = | |
1454 _dartObjectComputer.concatenate(node, result, string.accept(this)); | |
1455 } | |
1456 } | |
1457 return result; | |
1458 } | |
1459 | |
1460 @override | |
1461 DartObjectImpl visitBinaryExpression(BinaryExpression node) { | |
1462 DartObjectImpl leftResult = node.leftOperand.accept(this); | |
1463 DartObjectImpl rightResult = node.rightOperand.accept(this); | |
1464 TokenType operatorType = node.operator.type; | |
1465 // 'null' is almost never good operand | |
1466 if (operatorType != TokenType.BANG_EQ && operatorType != TokenType.EQ_EQ) { | |
1467 if (leftResult != null && leftResult.isNull || | |
1468 rightResult != null && rightResult.isNull) { | |
1469 _error(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
1470 return null; | |
1471 } | |
1472 } | |
1473 // evaluate operator | |
1474 while (true) { | |
1475 if (operatorType == TokenType.AMPERSAND) { | |
1476 return _dartObjectComputer.bitAnd(node, leftResult, rightResult); | |
1477 } else if (operatorType == TokenType.AMPERSAND_AMPERSAND) { | |
1478 return _dartObjectComputer.logicalAnd(node, leftResult, rightResult); | |
1479 } else if (operatorType == TokenType.BANG_EQ) { | |
1480 return _dartObjectComputer.notEqual(node, leftResult, rightResult); | |
1481 } else if (operatorType == TokenType.BAR) { | |
1482 return _dartObjectComputer.bitOr(node, leftResult, rightResult); | |
1483 } else if (operatorType == TokenType.BAR_BAR) { | |
1484 return _dartObjectComputer.logicalOr(node, leftResult, rightResult); | |
1485 } else if (operatorType == TokenType.CARET) { | |
1486 return _dartObjectComputer.bitXor(node, leftResult, rightResult); | |
1487 } else if (operatorType == TokenType.EQ_EQ) { | |
1488 return _dartObjectComputer.equalEqual(node, leftResult, rightResult); | |
1489 } else if (operatorType == TokenType.GT) { | |
1490 return _dartObjectComputer.greaterThan(node, leftResult, rightResult); | |
1491 } else if (operatorType == TokenType.GT_EQ) { | |
1492 return _dartObjectComputer.greaterThanOrEqual( | |
1493 node, leftResult, rightResult); | |
1494 } else if (operatorType == TokenType.GT_GT) { | |
1495 return _dartObjectComputer.shiftRight(node, leftResult, rightResult); | |
1496 } else if (operatorType == TokenType.LT) { | |
1497 return _dartObjectComputer.lessThan(node, leftResult, rightResult); | |
1498 } else if (operatorType == TokenType.LT_EQ) { | |
1499 return _dartObjectComputer.lessThanOrEqual( | |
1500 node, leftResult, rightResult); | |
1501 } else if (operatorType == TokenType.LT_LT) { | |
1502 return _dartObjectComputer.shiftLeft(node, leftResult, rightResult); | |
1503 } else if (operatorType == TokenType.MINUS) { | |
1504 return _dartObjectComputer.minus(node, leftResult, rightResult); | |
1505 } else if (operatorType == TokenType.PERCENT) { | |
1506 return _dartObjectComputer.remainder(node, leftResult, rightResult); | |
1507 } else if (operatorType == TokenType.PLUS) { | |
1508 return _dartObjectComputer.add(node, leftResult, rightResult); | |
1509 } else if (operatorType == TokenType.STAR) { | |
1510 return _dartObjectComputer.times(node, leftResult, rightResult); | |
1511 } else if (operatorType == TokenType.SLASH) { | |
1512 return _dartObjectComputer.divide(node, leftResult, rightResult); | |
1513 } else if (operatorType == TokenType.TILDE_SLASH) { | |
1514 return _dartObjectComputer.integerDivide(node, leftResult, rightResult); | |
1515 } else { | |
1516 // TODO(brianwilkerson) Figure out which error to report. | |
1517 _error(node, null); | |
1518 return null; | |
1519 } | |
1520 break; | |
1521 } | |
1522 } | |
1523 | |
1524 @override | |
1525 DartObjectImpl visitBooleanLiteral(BooleanLiteral node) => | |
1526 new DartObjectImpl(_typeProvider.boolType, BoolState.from(node.value)); | |
1527 | |
1528 @override | |
1529 DartObjectImpl visitConditionalExpression(ConditionalExpression node) { | |
1530 Expression condition = node.condition; | |
1531 DartObjectImpl conditionResult = condition.accept(this); | |
1532 DartObjectImpl thenResult = node.thenExpression.accept(this); | |
1533 DartObjectImpl elseResult = node.elseExpression.accept(this); | |
1534 if (conditionResult == null) { | |
1535 return conditionResult; | |
1536 } else if (!conditionResult.isBool) { | |
1537 _errorReporter.reportErrorForNode( | |
1538 CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL, condition); | |
1539 return null; | |
1540 } else if (thenResult == null) { | |
1541 return thenResult; | |
1542 } else if (elseResult == null) { | |
1543 return elseResult; | |
1544 } | |
1545 conditionResult = | |
1546 _dartObjectComputer.applyBooleanConversion(condition, conditionResult); | |
1547 if (conditionResult == null) { | |
1548 return conditionResult; | |
1549 } | |
1550 if (conditionResult.isTrue) { | |
1551 return thenResult; | |
1552 } else if (conditionResult.isFalse) { | |
1553 return elseResult; | |
1554 } | |
1555 ParameterizedType thenType = thenResult.type; | |
1556 ParameterizedType elseType = elseResult.type; | |
1557 return new DartObjectImpl.validWithUnknownValue( | |
1558 _typeSystem.getLeastUpperBound(thenType, elseType) as InterfaceType); | |
1559 } | |
1560 | |
1561 @override | |
1562 DartObjectImpl visitDoubleLiteral(DoubleLiteral node) => | |
1563 new DartObjectImpl(_typeProvider.doubleType, new DoubleState(node.value)); | |
1564 | |
1565 @override | |
1566 DartObjectImpl visitInstanceCreationExpression( | |
1567 InstanceCreationExpression node) { | |
1568 if (!node.isConst) { | |
1569 // TODO(brianwilkerson) Figure out which error to report. | |
1570 _error(node, null); | |
1571 return null; | |
1572 } | |
1573 ConstructorElement constructor = node.staticElement; | |
1574 if (constructor == null) { | |
1575 // Couldn't resolve the constructor so we can't compute a value. No | |
1576 // problem - the error has already been reported. | |
1577 return null; | |
1578 } | |
1579 return evaluationEngine.evaluateConstructorCall( | |
1580 node, node.argumentList.arguments, constructor, this, _errorReporter); | |
1581 } | |
1582 | |
1583 @override | |
1584 DartObjectImpl visitIntegerLiteral(IntegerLiteral node) => | |
1585 new DartObjectImpl(_typeProvider.intType, new IntState(node.value)); | |
1586 | |
1587 @override | |
1588 DartObjectImpl visitInterpolationExpression(InterpolationExpression node) { | |
1589 DartObjectImpl result = node.expression.accept(this); | |
1590 if (result != null && !result.isBoolNumStringOrNull) { | |
1591 _error(node, CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING); | |
1592 return null; | |
1593 } | |
1594 return _dartObjectComputer.performToString(node, result); | |
1595 } | |
1596 | |
1597 @override | |
1598 DartObjectImpl visitInterpolationString(InterpolationString node) => | |
1599 new DartObjectImpl(_typeProvider.stringType, new StringState(node.value)); | |
1600 | |
1601 @override | |
1602 DartObjectImpl visitListLiteral(ListLiteral node) { | |
1603 if (node.constKeyword == null) { | |
1604 _errorReporter.reportErrorForNode( | |
1605 CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL, node); | |
1606 return null; | |
1607 } | |
1608 bool errorOccurred = false; | |
1609 List<DartObjectImpl> elements = new List<DartObjectImpl>(); | |
1610 for (Expression element in node.elements) { | |
1611 DartObjectImpl elementResult = element.accept(this); | |
1612 if (elementResult == null) { | |
1613 errorOccurred = true; | |
1614 } else { | |
1615 elements.add(elementResult); | |
1616 } | |
1617 } | |
1618 if (errorOccurred) { | |
1619 return null; | |
1620 } | |
1621 DartType elementType = _typeProvider.dynamicType; | |
1622 if (node.typeArguments != null && | |
1623 node.typeArguments.arguments.length == 1) { | |
1624 DartType type = node.typeArguments.arguments[0].type; | |
1625 if (type != null) { | |
1626 elementType = type; | |
1627 } | |
1628 } | |
1629 InterfaceType listType = _typeProvider.listType.substitute4([elementType]); | |
1630 return new DartObjectImpl(listType, new ListState(elements)); | |
1631 } | |
1632 | |
1633 @override | |
1634 DartObjectImpl visitMapLiteral(MapLiteral node) { | |
1635 if (node.constKeyword == null) { | |
1636 _errorReporter.reportErrorForNode( | |
1637 CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL, node); | |
1638 return null; | |
1639 } | |
1640 bool errorOccurred = false; | |
1641 HashMap<DartObjectImpl, DartObjectImpl> map = | |
1642 new HashMap<DartObjectImpl, DartObjectImpl>(); | |
1643 for (MapLiteralEntry entry in node.entries) { | |
1644 DartObjectImpl keyResult = entry.key.accept(this); | |
1645 DartObjectImpl valueResult = entry.value.accept(this); | |
1646 if (keyResult == null || valueResult == null) { | |
1647 errorOccurred = true; | |
1648 } else { | |
1649 map[keyResult] = valueResult; | |
1650 } | |
1651 } | |
1652 if (errorOccurred) { | |
1653 return null; | |
1654 } | |
1655 DartType keyType = _typeProvider.dynamicType; | |
1656 DartType valueType = _typeProvider.dynamicType; | |
1657 if (node.typeArguments != null && | |
1658 node.typeArguments.arguments.length == 2) { | |
1659 DartType keyTypeCandidate = node.typeArguments.arguments[0].type; | |
1660 if (keyTypeCandidate != null) { | |
1661 keyType = keyTypeCandidate; | |
1662 } | |
1663 DartType valueTypeCandidate = node.typeArguments.arguments[1].type; | |
1664 if (valueTypeCandidate != null) { | |
1665 valueType = valueTypeCandidate; | |
1666 } | |
1667 } | |
1668 InterfaceType mapType = | |
1669 _typeProvider.mapType.substitute4([keyType, valueType]); | |
1670 return new DartObjectImpl(mapType, new MapState(map)); | |
1671 } | |
1672 | |
1673 @override | |
1674 DartObjectImpl visitMethodInvocation(MethodInvocation node) { | |
1675 Element element = node.methodName.staticElement; | |
1676 if (element is FunctionElement) { | |
1677 FunctionElement function = element; | |
1678 if (function.name == "identical") { | |
1679 NodeList<Expression> arguments = node.argumentList.arguments; | |
1680 if (arguments.length == 2) { | |
1681 Element enclosingElement = function.enclosingElement; | |
1682 if (enclosingElement is CompilationUnitElement) { | |
1683 LibraryElement library = enclosingElement.library; | |
1684 if (library.isDartCore) { | |
1685 DartObjectImpl leftArgument = arguments[0].accept(this); | |
1686 DartObjectImpl rightArgument = arguments[1].accept(this); | |
1687 return _dartObjectComputer.isIdentical( | |
1688 node, leftArgument, rightArgument); | |
1689 } | |
1690 } | |
1691 } | |
1692 } | |
1693 } | |
1694 // TODO(brianwilkerson) Figure out which error to report. | |
1695 _error(node, null); | |
1696 return null; | |
1697 } | |
1698 | |
1699 @override | |
1700 DartObjectImpl visitNamedExpression(NamedExpression node) => | |
1701 node.expression.accept(this); | |
1702 | |
1703 @override | |
1704 DartObjectImpl visitNode(AstNode node) { | |
1705 // TODO(brianwilkerson) Figure out which error to report. | |
1706 _error(node, null); | |
1707 return null; | |
1708 } | |
1709 | |
1710 @override | |
1711 DartObjectImpl visitNullLiteral(NullLiteral node) => _typeProvider.nullObject; | |
1712 | |
1713 @override | |
1714 DartObjectImpl visitParenthesizedExpression(ParenthesizedExpression node) => | |
1715 node.expression.accept(this); | |
1716 | |
1717 @override | |
1718 DartObjectImpl visitPrefixedIdentifier(PrefixedIdentifier node) { | |
1719 SimpleIdentifier prefixNode = node.prefix; | |
1720 Element prefixElement = prefixNode.staticElement; | |
1721 // String.length | |
1722 if (prefixElement is! PrefixElement && prefixElement is! ClassElement) { | |
1723 DartObjectImpl prefixResult = node.prefix.accept(this); | |
1724 if (_isStringLength(prefixResult, node.identifier)) { | |
1725 return prefixResult.stringLength(_typeProvider); | |
1726 } | |
1727 } | |
1728 // importPrefix.CONST | |
1729 if (prefixElement is! PrefixElement) { | |
1730 DartObjectImpl prefixResult = prefixNode.accept(this); | |
1731 if (prefixResult == null) { | |
1732 // The error has already been reported. | |
1733 return null; | |
1734 } | |
1735 } | |
1736 // validate prefixed identifier | |
1737 return _getConstantValue(node, node.staticElement); | |
1738 } | |
1739 | |
1740 @override | |
1741 DartObjectImpl visitPrefixExpression(PrefixExpression node) { | |
1742 DartObjectImpl operand = node.operand.accept(this); | |
1743 if (operand != null && operand.isNull) { | |
1744 _error(node, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
1745 return null; | |
1746 } | |
1747 while (true) { | |
1748 if (node.operator.type == TokenType.BANG) { | |
1749 return _dartObjectComputer.logicalNot(node, operand); | |
1750 } else if (node.operator.type == TokenType.TILDE) { | |
1751 return _dartObjectComputer.bitNot(node, operand); | |
1752 } else if (node.operator.type == TokenType.MINUS) { | |
1753 return _dartObjectComputer.negated(node, operand); | |
1754 } else { | |
1755 // TODO(brianwilkerson) Figure out which error to report. | |
1756 _error(node, null); | |
1757 return null; | |
1758 } | |
1759 break; | |
1760 } | |
1761 } | |
1762 | |
1763 @override | |
1764 DartObjectImpl visitPropertyAccess(PropertyAccess node) { | |
1765 if (node.target != null) { | |
1766 DartObjectImpl prefixResult = node.target.accept(this); | |
1767 if (_isStringLength(prefixResult, node.propertyName)) { | |
1768 return prefixResult.stringLength(_typeProvider); | |
1769 } | |
1770 } | |
1771 return _getConstantValue(node, node.propertyName.staticElement); | |
1772 } | |
1773 | |
1774 @override | |
1775 DartObjectImpl visitSimpleIdentifier(SimpleIdentifier node) { | |
1776 if (_lexicalEnvironment != null && | |
1777 _lexicalEnvironment.containsKey(node.name)) { | |
1778 return _lexicalEnvironment[node.name]; | |
1779 } | |
1780 return _getConstantValue(node, node.staticElement); | |
1781 } | |
1782 | |
1783 @override | |
1784 DartObjectImpl visitSimpleStringLiteral(SimpleStringLiteral node) => | |
1785 new DartObjectImpl(_typeProvider.stringType, new StringState(node.value)); | |
1786 | |
1787 @override | |
1788 DartObjectImpl visitStringInterpolation(StringInterpolation node) { | |
1789 DartObjectImpl result = null; | |
1790 bool first = true; | |
1791 for (InterpolationElement element in node.elements) { | |
1792 if (first) { | |
1793 result = element.accept(this); | |
1794 first = false; | |
1795 } else { | |
1796 result = | |
1797 _dartObjectComputer.concatenate(node, result, element.accept(this)); | |
1798 } | |
1799 } | |
1800 return result; | |
1801 } | |
1802 | |
1803 @override | |
1804 DartObjectImpl visitSymbolLiteral(SymbolLiteral node) { | |
1805 StringBuffer buffer = new StringBuffer(); | |
1806 List<Token> components = node.components; | |
1807 for (int i = 0; i < components.length; i++) { | |
1808 if (i > 0) { | |
1809 buffer.writeCharCode(0x2E); | |
1810 } | |
1811 buffer.write(components[i].lexeme); | |
1812 } | |
1813 return new DartObjectImpl( | |
1814 _typeProvider.symbolType, new SymbolState(buffer.toString())); | |
1815 } | |
1816 | |
1817 /** | |
1818 * Create an error associated with the given [node]. The error will have the | |
1819 * given error [code]. | |
1820 */ | |
1821 void _error(AstNode node, ErrorCode code) { | |
1822 _errorReporter.reportErrorForNode( | |
1823 code == null ? CompileTimeErrorCode.INVALID_CONSTANT : code, node); | |
1824 } | |
1825 | |
1826 /** | |
1827 * Return the constant value of the static constant represented by the given | |
1828 * [element]. The [node] is the node to be used if an error needs to be | |
1829 * reported. | |
1830 */ | |
1831 DartObjectImpl _getConstantValue(AstNode node, Element element) { | |
1832 if (element is PropertyAccessorElement) { | |
1833 element = (element as PropertyAccessorElement).variable; | |
1834 } | |
1835 if (element is VariableElementImpl) { | |
1836 VariableElementImpl variableElementImpl = element; | |
1837 evaluationEngine.validator.beforeGetEvaluationResult(element); | |
1838 EvaluationResultImpl value = variableElementImpl.evaluationResult; | |
1839 if (variableElementImpl.isConst && value != null) { | |
1840 return value.value; | |
1841 } | |
1842 } else if (element is ExecutableElement) { | |
1843 ExecutableElement function = element; | |
1844 if (function.isStatic) { | |
1845 ParameterizedType functionType = function.type; | |
1846 if (functionType == null) { | |
1847 functionType = _typeProvider.functionType; | |
1848 } | |
1849 return new DartObjectImpl(functionType, new FunctionState(function)); | |
1850 } | |
1851 } else if (element is ClassElement || | |
1852 element is FunctionTypeAliasElement || | |
1853 element is DynamicElementImpl) { | |
1854 return new DartObjectImpl(_typeProvider.typeType, new TypeState(element)); | |
1855 } | |
1856 // TODO(brianwilkerson) Figure out which error to report. | |
1857 _error(node, null); | |
1858 return null; | |
1859 } | |
1860 | |
1861 /** | |
1862 * Return `true` if the given [targetResult] represents a string and the | |
1863 * [identifier] is "length". | |
1864 */ | |
1865 bool _isStringLength( | |
1866 DartObjectImpl targetResult, SimpleIdentifier identifier) { | |
1867 if (targetResult == null || targetResult.type != _typeProvider.stringType) { | |
1868 return false; | |
1869 } | |
1870 return identifier.name == 'length'; | |
1871 } | |
1872 | |
1873 /** | |
1874 * Return the value of the given [expression], or a representation of 'null' | |
1875 * if the expression cannot be evaluated. | |
1876 */ | |
1877 DartObjectImpl _valueOf(Expression expression) { | |
1878 DartObjectImpl expressionValue = expression.accept(this); | |
1879 if (expressionValue != null) { | |
1880 return expressionValue; | |
1881 } | |
1882 return _typeProvider.nullObject; | |
1883 } | |
1884 } | |
1885 | |
1886 /** | |
1887 * The state of a Dart object. | |
1888 */ | |
1889 abstract class DartObject { | |
1890 /** | |
1891 * Return the boolean value of this object, or `null` if either the value of | |
1892 * this object is not known or this object is not of type 'bool'. | |
1893 */ | |
1894 bool get boolValue; | |
1895 | |
1896 /** | |
1897 * Return the floating point value of this object, or `null` if either the | |
1898 * value of this object is not known or this object is not of type 'double'. | |
1899 */ | |
1900 double get doubleValue; | |
1901 | |
1902 /** | |
1903 * Return `true` if this object's value can be represented exactly. | |
1904 */ | |
1905 bool get hasExactValue; | |
1906 | |
1907 /** | |
1908 * Return the integer value of this object, or `null` if either the value of | |
1909 * this object is not known or this object is not of type 'int'. | |
1910 */ | |
1911 int get intValue; | |
1912 | |
1913 /** | |
1914 * Return `true` if this object represents the value 'false'. | |
1915 */ | |
1916 bool get isFalse; | |
1917 | |
1918 /** | |
1919 * Return `true` if this object represents the value 'null'. | |
1920 */ | |
1921 bool get isNull; | |
1922 | |
1923 /** | |
1924 * Return `true` if this object represents the value 'true'. | |
1925 */ | |
1926 bool get isTrue; | |
1927 | |
1928 /** | |
1929 * Return the string value of this object, or `null` if either the value of | |
1930 * this object is not known or this object is not of type 'String'. | |
1931 */ | |
1932 String get stringValue; | |
1933 | |
1934 /** | |
1935 * Return the run-time type of this object. | |
1936 */ | |
1937 ParameterizedType get type; | |
1938 | |
1939 /** | |
1940 * Return this object's value if it can be represented exactly, or `null` if | |
1941 * either the value cannot be represented exactly or if the value is `null`. | |
1942 * Clients should use [hasExactValue] to distinguish between these two cases. | |
1943 */ | |
1944 Object get value; | |
1945 } | |
1946 | |
1947 /** | |
1948 * A utility class that contains methods for manipulating instances of a Dart | |
1949 * class and for collecting errors during evaluation. | |
1950 */ | |
1951 class DartObjectComputer { | |
1952 /** | |
1953 * The error reporter that we are using to collect errors. | |
1954 */ | |
1955 final ErrorReporter _errorReporter; | |
1956 | |
1957 /** | |
1958 * The type provider used to create objects of the appropriate types, and to | |
1959 * identify when an object is of a built-in type. | |
1960 */ | |
1961 final TypeProvider _typeProvider; | |
1962 | |
1963 DartObjectComputer(this._errorReporter, this._typeProvider); | |
1964 | |
1965 DartObjectImpl add(BinaryExpression node, DartObjectImpl leftOperand, | |
1966 DartObjectImpl rightOperand) { | |
1967 if (leftOperand != null && rightOperand != null) { | |
1968 try { | |
1969 return leftOperand.add(_typeProvider, rightOperand); | |
1970 } on EvaluationException catch (exception) { | |
1971 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
1972 return null; | |
1973 } | |
1974 } | |
1975 return null; | |
1976 } | |
1977 | |
1978 /** | |
1979 * Return the result of applying boolean conversion to the [evaluationResult]. | |
1980 * The [node] is the node against which errors should be reported. | |
1981 */ | |
1982 DartObjectImpl applyBooleanConversion( | |
1983 AstNode node, DartObjectImpl evaluationResult) { | |
1984 if (evaluationResult != null) { | |
1985 try { | |
1986 return evaluationResult.convertToBool(_typeProvider); | |
1987 } on EvaluationException catch (exception) { | |
1988 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
1989 } | |
1990 } | |
1991 return null; | |
1992 } | |
1993 | |
1994 DartObjectImpl bitAnd(BinaryExpression node, DartObjectImpl leftOperand, | |
1995 DartObjectImpl rightOperand) { | |
1996 if (leftOperand != null && rightOperand != null) { | |
1997 try { | |
1998 return leftOperand.bitAnd(_typeProvider, rightOperand); | |
1999 } on EvaluationException catch (exception) { | |
2000 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2001 } | |
2002 } | |
2003 return null; | |
2004 } | |
2005 | |
2006 DartObjectImpl bitNot(Expression node, DartObjectImpl evaluationResult) { | |
2007 if (evaluationResult != null) { | |
2008 try { | |
2009 return evaluationResult.bitNot(_typeProvider); | |
2010 } on EvaluationException catch (exception) { | |
2011 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2012 } | |
2013 } | |
2014 return null; | |
2015 } | |
2016 | |
2017 DartObjectImpl bitOr(BinaryExpression node, DartObjectImpl leftOperand, | |
2018 DartObjectImpl rightOperand) { | |
2019 if (leftOperand != null && rightOperand != null) { | |
2020 try { | |
2021 return leftOperand.bitOr(_typeProvider, rightOperand); | |
2022 } on EvaluationException catch (exception) { | |
2023 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2024 } | |
2025 } | |
2026 return null; | |
2027 } | |
2028 | |
2029 DartObjectImpl bitXor(BinaryExpression node, DartObjectImpl leftOperand, | |
2030 DartObjectImpl rightOperand) { | |
2031 if (leftOperand != null && rightOperand != null) { | |
2032 try { | |
2033 return leftOperand.bitXor(_typeProvider, rightOperand); | |
2034 } on EvaluationException catch (exception) { | |
2035 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2036 } | |
2037 } | |
2038 return null; | |
2039 } | |
2040 | |
2041 DartObjectImpl concatenate(Expression node, DartObjectImpl leftOperand, | |
2042 DartObjectImpl rightOperand) { | |
2043 if (leftOperand != null && rightOperand != null) { | |
2044 try { | |
2045 return leftOperand.concatenate(_typeProvider, rightOperand); | |
2046 } on EvaluationException catch (exception) { | |
2047 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2048 } | |
2049 } | |
2050 return null; | |
2051 } | |
2052 | |
2053 DartObjectImpl divide(BinaryExpression node, DartObjectImpl leftOperand, | |
2054 DartObjectImpl rightOperand) { | |
2055 if (leftOperand != null && rightOperand != null) { | |
2056 try { | |
2057 return leftOperand.divide(_typeProvider, rightOperand); | |
2058 } on EvaluationException catch (exception) { | |
2059 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2060 } | |
2061 } | |
2062 return null; | |
2063 } | |
2064 | |
2065 DartObjectImpl equalEqual(Expression node, DartObjectImpl leftOperand, | |
2066 DartObjectImpl rightOperand) { | |
2067 if (leftOperand != null && rightOperand != null) { | |
2068 try { | |
2069 return leftOperand.equalEqual(_typeProvider, rightOperand); | |
2070 } on EvaluationException catch (exception) { | |
2071 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2072 } | |
2073 } | |
2074 return null; | |
2075 } | |
2076 | |
2077 DartObjectImpl greaterThan(BinaryExpression node, DartObjectImpl leftOperand, | |
2078 DartObjectImpl rightOperand) { | |
2079 if (leftOperand != null && rightOperand != null) { | |
2080 try { | |
2081 return leftOperand.greaterThan(_typeProvider, rightOperand); | |
2082 } on EvaluationException catch (exception) { | |
2083 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2084 } | |
2085 } | |
2086 return null; | |
2087 } | |
2088 | |
2089 DartObjectImpl greaterThanOrEqual(BinaryExpression node, | |
2090 DartObjectImpl leftOperand, DartObjectImpl rightOperand) { | |
2091 if (leftOperand != null && rightOperand != null) { | |
2092 try { | |
2093 return leftOperand.greaterThanOrEqual(_typeProvider, rightOperand); | |
2094 } on EvaluationException catch (exception) { | |
2095 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2096 } | |
2097 } | |
2098 return null; | |
2099 } | |
2100 | |
2101 DartObjectImpl integerDivide(BinaryExpression node, | |
2102 DartObjectImpl leftOperand, DartObjectImpl rightOperand) { | |
2103 if (leftOperand != null && rightOperand != null) { | |
2104 try { | |
2105 return leftOperand.integerDivide(_typeProvider, rightOperand); | |
2106 } on EvaluationException catch (exception) { | |
2107 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2108 } | |
2109 } | |
2110 return null; | |
2111 } | |
2112 | |
2113 DartObjectImpl isIdentical(Expression node, DartObjectImpl leftOperand, | |
2114 DartObjectImpl rightOperand) { | |
2115 if (leftOperand != null && rightOperand != null) { | |
2116 try { | |
2117 return leftOperand.isIdentical(_typeProvider, rightOperand); | |
2118 } on EvaluationException catch (exception) { | |
2119 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2120 } | |
2121 } | |
2122 return null; | |
2123 } | |
2124 | |
2125 DartObjectImpl lessThan(BinaryExpression node, DartObjectImpl leftOperand, | |
2126 DartObjectImpl rightOperand) { | |
2127 if (leftOperand != null && rightOperand != null) { | |
2128 try { | |
2129 return leftOperand.lessThan(_typeProvider, rightOperand); | |
2130 } on EvaluationException catch (exception) { | |
2131 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2132 } | |
2133 } | |
2134 return null; | |
2135 } | |
2136 | |
2137 DartObjectImpl lessThanOrEqual(BinaryExpression node, | |
2138 DartObjectImpl leftOperand, DartObjectImpl rightOperand) { | |
2139 if (leftOperand != null && rightOperand != null) { | |
2140 try { | |
2141 return leftOperand.lessThanOrEqual(_typeProvider, rightOperand); | |
2142 } on EvaluationException catch (exception) { | |
2143 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2144 } | |
2145 } | |
2146 return null; | |
2147 } | |
2148 | |
2149 DartObjectImpl logicalAnd(BinaryExpression node, DartObjectImpl leftOperand, | |
2150 DartObjectImpl rightOperand) { | |
2151 if (leftOperand != null && rightOperand != null) { | |
2152 try { | |
2153 return leftOperand.logicalAnd(_typeProvider, rightOperand); | |
2154 } on EvaluationException catch (exception) { | |
2155 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2156 } | |
2157 } | |
2158 return null; | |
2159 } | |
2160 | |
2161 DartObjectImpl logicalNot(Expression node, DartObjectImpl evaluationResult) { | |
2162 if (evaluationResult != null) { | |
2163 try { | |
2164 return evaluationResult.logicalNot(_typeProvider); | |
2165 } on EvaluationException catch (exception) { | |
2166 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2167 } | |
2168 } | |
2169 return null; | |
2170 } | |
2171 | |
2172 DartObjectImpl logicalOr(BinaryExpression node, DartObjectImpl leftOperand, | |
2173 DartObjectImpl rightOperand) { | |
2174 if (leftOperand != null && rightOperand != null) { | |
2175 try { | |
2176 return leftOperand.logicalOr(_typeProvider, rightOperand); | |
2177 } on EvaluationException catch (exception) { | |
2178 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2179 } | |
2180 } | |
2181 return null; | |
2182 } | |
2183 | |
2184 DartObjectImpl minus(BinaryExpression node, DartObjectImpl leftOperand, | |
2185 DartObjectImpl rightOperand) { | |
2186 if (leftOperand != null && rightOperand != null) { | |
2187 try { | |
2188 return leftOperand.minus(_typeProvider, rightOperand); | |
2189 } on EvaluationException catch (exception) { | |
2190 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2191 } | |
2192 } | |
2193 return null; | |
2194 } | |
2195 | |
2196 DartObjectImpl negated(Expression node, DartObjectImpl evaluationResult) { | |
2197 if (evaluationResult != null) { | |
2198 try { | |
2199 return evaluationResult.negated(_typeProvider); | |
2200 } on EvaluationException catch (exception) { | |
2201 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2202 } | |
2203 } | |
2204 return null; | |
2205 } | |
2206 | |
2207 DartObjectImpl notEqual(BinaryExpression node, DartObjectImpl leftOperand, | |
2208 DartObjectImpl rightOperand) { | |
2209 if (leftOperand != null && rightOperand != null) { | |
2210 try { | |
2211 return leftOperand.notEqual(_typeProvider, rightOperand); | |
2212 } on EvaluationException catch (exception) { | |
2213 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2214 } | |
2215 } | |
2216 return null; | |
2217 } | |
2218 | |
2219 DartObjectImpl performToString( | |
2220 AstNode node, DartObjectImpl evaluationResult) { | |
2221 if (evaluationResult != null) { | |
2222 try { | |
2223 return evaluationResult.performToString(_typeProvider); | |
2224 } on EvaluationException catch (exception) { | |
2225 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2226 } | |
2227 } | |
2228 return null; | |
2229 } | |
2230 | |
2231 DartObjectImpl remainder(BinaryExpression node, DartObjectImpl leftOperand, | |
2232 DartObjectImpl rightOperand) { | |
2233 if (leftOperand != null && rightOperand != null) { | |
2234 try { | |
2235 return leftOperand.remainder(_typeProvider, rightOperand); | |
2236 } on EvaluationException catch (exception) { | |
2237 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2238 } | |
2239 } | |
2240 return null; | |
2241 } | |
2242 | |
2243 DartObjectImpl shiftLeft(BinaryExpression node, DartObjectImpl leftOperand, | |
2244 DartObjectImpl rightOperand) { | |
2245 if (leftOperand != null && rightOperand != null) { | |
2246 try { | |
2247 return leftOperand.shiftLeft(_typeProvider, rightOperand); | |
2248 } on EvaluationException catch (exception) { | |
2249 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2250 } | |
2251 } | |
2252 return null; | |
2253 } | |
2254 | |
2255 DartObjectImpl shiftRight(BinaryExpression node, DartObjectImpl leftOperand, | |
2256 DartObjectImpl rightOperand) { | |
2257 if (leftOperand != null && rightOperand != null) { | |
2258 try { | |
2259 return leftOperand.shiftRight(_typeProvider, rightOperand); | |
2260 } on EvaluationException catch (exception) { | |
2261 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2262 } | |
2263 } | |
2264 return null; | |
2265 } | |
2266 | |
2267 /** | |
2268 * Return the result of invoking the 'length' getter on the | |
2269 * [evaluationResult]. The [node] is the node against which errors should be | |
2270 * reported. | |
2271 */ | |
2272 EvaluationResultImpl stringLength( | |
2273 Expression node, EvaluationResultImpl evaluationResult) { | |
2274 if (evaluationResult.value != null) { | |
2275 try { | |
2276 return new EvaluationResultImpl( | |
2277 evaluationResult.value.stringLength(_typeProvider)); | |
2278 } on EvaluationException catch (exception) { | |
2279 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2280 } | |
2281 } | |
2282 return new EvaluationResultImpl(null); | |
2283 } | |
2284 | |
2285 DartObjectImpl times(BinaryExpression node, DartObjectImpl leftOperand, | |
2286 DartObjectImpl rightOperand) { | |
2287 if (leftOperand != null && rightOperand != null) { | |
2288 try { | |
2289 return leftOperand.times(_typeProvider, rightOperand); | |
2290 } on EvaluationException catch (exception) { | |
2291 _errorReporter.reportErrorForNode(exception.errorCode, node); | |
2292 } | |
2293 } | |
2294 return null; | |
2295 } | |
2296 } | |
2297 | |
2298 /** | |
2299 * An instance of a Dart class. | |
2300 */ | |
2301 class DartObjectImpl implements DartObject { | |
2302 /** | |
2303 * An empty list of objects. | |
2304 */ | |
2305 static const List<DartObjectImpl> EMPTY_LIST = const <DartObjectImpl>[]; | |
2306 | |
2307 /** | |
2308 * The run-time type of this object. | |
2309 */ | |
2310 final ParameterizedType type; | |
2311 | |
2312 /** | |
2313 * The state of the object. | |
2314 */ | |
2315 final InstanceState _state; | |
2316 | |
2317 /** | |
2318 * Initialize a newly created object to have the given [type] and [_state]. | |
2319 */ | |
2320 DartObjectImpl(this.type, this._state); | |
2321 | |
2322 /** | |
2323 * Create an object to represent an unknown value. | |
2324 */ | |
2325 factory DartObjectImpl.validWithUnknownValue(InterfaceType type) { | |
2326 if (type.element.library.isDartCore) { | |
2327 String typeName = type.name; | |
2328 if (typeName == "bool") { | |
2329 return new DartObjectImpl(type, BoolState.UNKNOWN_VALUE); | |
2330 } else if (typeName == "double") { | |
2331 return new DartObjectImpl(type, DoubleState.UNKNOWN_VALUE); | |
2332 } else if (typeName == "int") { | |
2333 return new DartObjectImpl(type, IntState.UNKNOWN_VALUE); | |
2334 } else if (typeName == "String") { | |
2335 return new DartObjectImpl(type, StringState.UNKNOWN_VALUE); | |
2336 } | |
2337 } | |
2338 return new DartObjectImpl(type, GenericState.UNKNOWN_VALUE); | |
2339 } | |
2340 | |
2341 @override | |
2342 bool get boolValue { | |
2343 if (_state is BoolState) { | |
2344 return (_state as BoolState).value; | |
2345 } | |
2346 return null; | |
2347 } | |
2348 | |
2349 @override | |
2350 double get doubleValue { | |
2351 if (_state is DoubleState) { | |
2352 return (_state as DoubleState).value; | |
2353 } | |
2354 return null; | |
2355 } | |
2356 | |
2357 HashMap<String, DartObjectImpl> get fields => _state.fields; | |
2358 | |
2359 @override | |
2360 bool get hasExactValue => _state.hasExactValue; | |
2361 | |
2362 @override | |
2363 int get hashCode => JenkinsSmiHash.hash2(type.hashCode, _state.hashCode); | |
2364 | |
2365 @override | |
2366 int get intValue { | |
2367 if (_state is IntState) { | |
2368 return (_state as IntState).value; | |
2369 } | |
2370 return null; | |
2371 } | |
2372 | |
2373 /** | |
2374 * Return `true` if this object represents an object whose type is 'bool'. | |
2375 */ | |
2376 bool get isBool => _state.isBool; | |
2377 | |
2378 /** | |
2379 * Return `true` if this object represents an object whose type is either | |
2380 * 'bool', 'num', 'String', or 'Null'. | |
2381 */ | |
2382 bool get isBoolNumStringOrNull => _state.isBoolNumStringOrNull; | |
2383 | |
2384 @override | |
2385 bool get isFalse => | |
2386 _state is BoolState && identical((_state as BoolState).value, false); | |
2387 | |
2388 @override | |
2389 bool get isNull => _state is NullState; | |
2390 | |
2391 @override | |
2392 bool get isTrue => | |
2393 _state is BoolState && identical((_state as BoolState).value, true); | |
2394 | |
2395 /** | |
2396 * Return `true` if this object represents an unknown value. | |
2397 */ | |
2398 bool get isUnknown => _state.isUnknown; | |
2399 | |
2400 /** | |
2401 * Return `true` if this object represents an instance of a user-defined | |
2402 * class. | |
2403 */ | |
2404 bool get isUserDefinedObject => _state is GenericState; | |
2405 | |
2406 @override | |
2407 String get stringValue { | |
2408 if (_state is StringState) { | |
2409 return (_state as StringState).value; | |
2410 } | |
2411 return null; | |
2412 } | |
2413 | |
2414 @override | |
2415 Object get value => _state.value; | |
2416 | |
2417 @override | |
2418 bool operator ==(Object object) { | |
2419 if (object is! DartObjectImpl) { | |
2420 return false; | |
2421 } | |
2422 DartObjectImpl dartObject = object as DartObjectImpl; | |
2423 return type == dartObject.type && _state == dartObject._state; | |
2424 } | |
2425 | |
2426 /** | |
2427 * Return the result of invoking the '+' operator on this object with the | |
2428 * given [rightOperand]. The [typeProvider] is the type provider used to find | |
2429 * known types. | |
2430 * | |
2431 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2432 * object of this kind. | |
2433 */ | |
2434 DartObjectImpl add(TypeProvider typeProvider, DartObjectImpl rightOperand) { | |
2435 InstanceState result = _state.add(rightOperand._state); | |
2436 if (result is IntState) { | |
2437 return new DartObjectImpl(typeProvider.intType, result); | |
2438 } else if (result is DoubleState) { | |
2439 return new DartObjectImpl(typeProvider.doubleType, result); | |
2440 } else if (result is NumState) { | |
2441 return new DartObjectImpl(typeProvider.numType, result); | |
2442 } else if (result is StringState) { | |
2443 return new DartObjectImpl(typeProvider.stringType, result); | |
2444 } | |
2445 // We should never get here. | |
2446 throw new IllegalStateException("add returned a ${result.runtimeType}"); | |
2447 } | |
2448 | |
2449 /** | |
2450 * Return the result of invoking the '&' operator on this object with the | |
2451 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2452 * types. | |
2453 * | |
2454 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2455 * object of this kind. | |
2456 */ | |
2457 DartObjectImpl bitAnd( | |
2458 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2459 new DartObjectImpl( | |
2460 typeProvider.intType, _state.bitAnd(rightOperand._state)); | |
2461 | |
2462 /** | |
2463 * Return the result of invoking the '~' operator on this object. The | |
2464 * [typeProvider] is the type provider used to find known types. | |
2465 * | |
2466 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2467 * object of this kind. | |
2468 */ | |
2469 DartObjectImpl bitNot(TypeProvider typeProvider) => | |
2470 new DartObjectImpl(typeProvider.intType, _state.bitNot()); | |
2471 | |
2472 /** | |
2473 * Return the result of invoking the '|' operator on this object with the | |
2474 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2475 * types. | |
2476 * | |
2477 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2478 * object of this kind. | |
2479 */ | |
2480 DartObjectImpl bitOr( | |
2481 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2482 new DartObjectImpl( | |
2483 typeProvider.intType, _state.bitOr(rightOperand._state)); | |
2484 | |
2485 /** | |
2486 * Return the result of invoking the '^' operator on this object with the | |
2487 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2488 * types. | |
2489 * | |
2490 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2491 * object of this kind. | |
2492 */ | |
2493 DartObjectImpl bitXor( | |
2494 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2495 new DartObjectImpl( | |
2496 typeProvider.intType, _state.bitXor(rightOperand._state)); | |
2497 | |
2498 /** | |
2499 * Return the result of invoking the ' ' operator on this object with the | |
2500 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2501 * types. | |
2502 * | |
2503 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2504 * object of this kind. | |
2505 */ | |
2506 DartObjectImpl concatenate( | |
2507 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2508 new DartObjectImpl( | |
2509 typeProvider.stringType, _state.concatenate(rightOperand._state)); | |
2510 | |
2511 /** | |
2512 * Return the result of applying boolean conversion to this object. The | |
2513 * [typeProvider] is the type provider used to find known types. | |
2514 * | |
2515 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2516 * object of this kind. | |
2517 */ | |
2518 DartObjectImpl convertToBool(TypeProvider typeProvider) { | |
2519 InterfaceType boolType = typeProvider.boolType; | |
2520 if (identical(type, boolType)) { | |
2521 return this; | |
2522 } | |
2523 return new DartObjectImpl(boolType, _state.convertToBool()); | |
2524 } | |
2525 | |
2526 /** | |
2527 * Return the result of invoking the '/' operator on this object with the | |
2528 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2529 * types. | |
2530 * | |
2531 * Throws an [EvaluationException] if the operator is not appropriate for | |
2532 * an object of this kind. | |
2533 */ | |
2534 DartObjectImpl divide( | |
2535 TypeProvider typeProvider, DartObjectImpl rightOperand) { | |
2536 InstanceState result = _state.divide(rightOperand._state); | |
2537 if (result is IntState) { | |
2538 return new DartObjectImpl(typeProvider.intType, result); | |
2539 } else if (result is DoubleState) { | |
2540 return new DartObjectImpl(typeProvider.doubleType, result); | |
2541 } else if (result is NumState) { | |
2542 return new DartObjectImpl(typeProvider.numType, result); | |
2543 } | |
2544 // We should never get here. | |
2545 throw new IllegalStateException("divide returned a ${result.runtimeType}"); | |
2546 } | |
2547 | |
2548 /** | |
2549 * Return the result of invoking the '==' operator on this object with the | |
2550 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2551 * types. | |
2552 * | |
2553 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2554 * object of this kind. | |
2555 */ | |
2556 DartObjectImpl equalEqual( | |
2557 TypeProvider typeProvider, DartObjectImpl rightOperand) { | |
2558 if (type != rightOperand.type) { | |
2559 String typeName = type.name; | |
2560 if (!(typeName == "bool" || | |
2561 typeName == "double" || | |
2562 typeName == "int" || | |
2563 typeName == "num" || | |
2564 typeName == "String" || | |
2565 typeName == "Null" || | |
2566 type.isDynamic)) { | |
2567 throw new EvaluationException( | |
2568 CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING); | |
2569 } | |
2570 } | |
2571 return new DartObjectImpl( | |
2572 typeProvider.boolType, _state.equalEqual(rightOperand._state)); | |
2573 } | |
2574 | |
2575 /** | |
2576 * Return the result of invoking the '>' operator on this object with the | |
2577 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2578 * types. | |
2579 * | |
2580 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2581 * object of this kind. | |
2582 */ | |
2583 DartObjectImpl greaterThan( | |
2584 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2585 new DartObjectImpl( | |
2586 typeProvider.boolType, _state.greaterThan(rightOperand._state)); | |
2587 | |
2588 /** | |
2589 * Return the result of invoking the '>=' operator on this object with the | |
2590 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2591 * types. | |
2592 * | |
2593 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2594 * object of this kind. | |
2595 */ | |
2596 DartObjectImpl greaterThanOrEqual(TypeProvider typeProvider, | |
2597 DartObjectImpl rightOperand) => new DartObjectImpl( | |
2598 typeProvider.boolType, _state.greaterThanOrEqual(rightOperand._state)); | |
2599 | |
2600 /** | |
2601 * Return the result of invoking the '~/' operator on this object with the | |
2602 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2603 * types. | |
2604 * | |
2605 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2606 * object of this kind. | |
2607 */ | |
2608 DartObjectImpl integerDivide( | |
2609 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2610 new DartObjectImpl( | |
2611 typeProvider.intType, _state.integerDivide(rightOperand._state)); | |
2612 | |
2613 /** | |
2614 * Return the result of invoking the identical function on this object with | |
2615 * the [rightOperand]. The [typeProvider] is the type provider used to find | |
2616 * known types. | |
2617 */ | |
2618 DartObjectImpl isIdentical( | |
2619 TypeProvider typeProvider, DartObjectImpl rightOperand) { | |
2620 return new DartObjectImpl( | |
2621 typeProvider.boolType, _state.isIdentical(rightOperand._state)); | |
2622 } | |
2623 | |
2624 /** | |
2625 * Return the result of invoking the '<' operator on this object with the | |
2626 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2627 * types. | |
2628 * | |
2629 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2630 * object of this kind. | |
2631 */ | |
2632 DartObjectImpl lessThan( | |
2633 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2634 new DartObjectImpl( | |
2635 typeProvider.boolType, _state.lessThan(rightOperand._state)); | |
2636 | |
2637 /** | |
2638 * Return the result of invoking the '<=' operator on this object with the | |
2639 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2640 * types. | |
2641 * | |
2642 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2643 * object of this kind. | |
2644 */ | |
2645 DartObjectImpl lessThanOrEqual( | |
2646 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2647 new DartObjectImpl( | |
2648 typeProvider.boolType, _state.lessThanOrEqual(rightOperand._state)); | |
2649 | |
2650 /** | |
2651 * Return the result of invoking the '&&' operator on this object with the | |
2652 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2653 * types. | |
2654 * | |
2655 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2656 * object of this kind. | |
2657 */ | |
2658 DartObjectImpl logicalAnd( | |
2659 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2660 new DartObjectImpl( | |
2661 typeProvider.boolType, _state.logicalAnd(rightOperand._state)); | |
2662 | |
2663 /** | |
2664 * Return the result of invoking the '!' operator on this object. The | |
2665 * [typeProvider] is the type provider used to find known types. | |
2666 * | |
2667 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2668 * object of this kind. | |
2669 */ | |
2670 DartObjectImpl logicalNot(TypeProvider typeProvider) => | |
2671 new DartObjectImpl(typeProvider.boolType, _state.logicalNot()); | |
2672 | |
2673 /** | |
2674 * Return the result of invoking the '||' operator on this object with the | |
2675 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2676 * types. | |
2677 * | |
2678 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2679 * object of this kind. | |
2680 */ | |
2681 DartObjectImpl logicalOr( | |
2682 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2683 new DartObjectImpl( | |
2684 typeProvider.boolType, _state.logicalOr(rightOperand._state)); | |
2685 | |
2686 /** | |
2687 * Return the result of invoking the '-' operator on this object with the | |
2688 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2689 * types. | |
2690 * | |
2691 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2692 * object of this kind. | |
2693 */ | |
2694 DartObjectImpl minus(TypeProvider typeProvider, DartObjectImpl rightOperand) { | |
2695 InstanceState result = _state.minus(rightOperand._state); | |
2696 if (result is IntState) { | |
2697 return new DartObjectImpl(typeProvider.intType, result); | |
2698 } else if (result is DoubleState) { | |
2699 return new DartObjectImpl(typeProvider.doubleType, result); | |
2700 } else if (result is NumState) { | |
2701 return new DartObjectImpl(typeProvider.numType, result); | |
2702 } | |
2703 // We should never get here. | |
2704 throw new IllegalStateException("minus returned a ${result.runtimeType}"); | |
2705 } | |
2706 | |
2707 /** | |
2708 * Return the result of invoking the '-' operator on this object. The | |
2709 * [typeProvider] is the type provider used to find known types. | |
2710 * | |
2711 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2712 * object of this kind. | |
2713 */ | |
2714 DartObjectImpl negated(TypeProvider typeProvider) { | |
2715 InstanceState result = _state.negated(); | |
2716 if (result is IntState) { | |
2717 return new DartObjectImpl(typeProvider.intType, result); | |
2718 } else if (result is DoubleState) { | |
2719 return new DartObjectImpl(typeProvider.doubleType, result); | |
2720 } else if (result is NumState) { | |
2721 return new DartObjectImpl(typeProvider.numType, result); | |
2722 } | |
2723 // We should never get here. | |
2724 throw new IllegalStateException("negated returned a ${result.runtimeType}"); | |
2725 } | |
2726 | |
2727 /** | |
2728 * Return the result of invoking the '!=' operator on this object with the | |
2729 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2730 * types. | |
2731 * | |
2732 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2733 * object of this kind. | |
2734 */ | |
2735 DartObjectImpl notEqual( | |
2736 TypeProvider typeProvider, DartObjectImpl rightOperand) { | |
2737 if (type != rightOperand.type) { | |
2738 String typeName = type.name; | |
2739 if (typeName != "bool" && | |
2740 typeName != "double" && | |
2741 typeName != "int" && | |
2742 typeName != "num" && | |
2743 typeName != "String") { | |
2744 return new DartObjectImpl(typeProvider.boolType, BoolState.TRUE_STATE); | |
2745 } | |
2746 } | |
2747 return new DartObjectImpl(typeProvider.boolType, | |
2748 _state.equalEqual(rightOperand._state).logicalNot()); | |
2749 } | |
2750 | |
2751 /** | |
2752 * Return the result of converting this object to a 'String'. The | |
2753 * [typeProvider] is the type provider used to find known types. | |
2754 * | |
2755 * Throws an [EvaluationException] if the object cannot be converted to a | |
2756 * 'String'. | |
2757 */ | |
2758 DartObjectImpl performToString(TypeProvider typeProvider) { | |
2759 InterfaceType stringType = typeProvider.stringType; | |
2760 if (identical(type, stringType)) { | |
2761 return this; | |
2762 } | |
2763 return new DartObjectImpl(stringType, _state.convertToString()); | |
2764 } | |
2765 | |
2766 /** | |
2767 * Return the result of invoking the '%' operator on this object with the | |
2768 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2769 * types. | |
2770 * | |
2771 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2772 * object of this kind. | |
2773 */ | |
2774 DartObjectImpl remainder( | |
2775 TypeProvider typeProvider, DartObjectImpl rightOperand) { | |
2776 InstanceState result = _state.remainder(rightOperand._state); | |
2777 if (result is IntState) { | |
2778 return new DartObjectImpl(typeProvider.intType, result); | |
2779 } else if (result is DoubleState) { | |
2780 return new DartObjectImpl(typeProvider.doubleType, result); | |
2781 } else if (result is NumState) { | |
2782 return new DartObjectImpl(typeProvider.numType, result); | |
2783 } | |
2784 // We should never get here. | |
2785 throw new IllegalStateException( | |
2786 "remainder returned a ${result.runtimeType}"); | |
2787 } | |
2788 | |
2789 /** | |
2790 * Return the result of invoking the '<<' operator on this object with | |
2791 * the [rightOperand]. The [typeProvider] is the type provider used to find | |
2792 * known types. | |
2793 * | |
2794 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2795 * object of this kind. | |
2796 */ | |
2797 DartObjectImpl shiftLeft( | |
2798 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2799 new DartObjectImpl( | |
2800 typeProvider.intType, _state.shiftLeft(rightOperand._state)); | |
2801 | |
2802 /** | |
2803 * Return the result of invoking the '>>' operator on this object with | |
2804 * the [rightOperand]. The [typeProvider] is the type provider used to find | |
2805 * known types. | |
2806 * | |
2807 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2808 * object of this kind. | |
2809 */ | |
2810 DartObjectImpl shiftRight( | |
2811 TypeProvider typeProvider, DartObjectImpl rightOperand) => | |
2812 new DartObjectImpl( | |
2813 typeProvider.intType, _state.shiftRight(rightOperand._state)); | |
2814 | |
2815 /** | |
2816 * Return the result of invoking the 'length' getter on this object. The | |
2817 * [typeProvider] is the type provider used to find known types. | |
2818 * | |
2819 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2820 * object of this kind. | |
2821 */ | |
2822 DartObjectImpl stringLength(TypeProvider typeProvider) => | |
2823 new DartObjectImpl(typeProvider.intType, _state.stringLength()); | |
2824 | |
2825 /** | |
2826 * Return the result of invoking the '*' operator on this object with the | |
2827 * [rightOperand]. The [typeProvider] is the type provider used to find known | |
2828 * types. | |
2829 * | |
2830 * Throws an [EvaluationException] if the operator is not appropriate for an | |
2831 * object of this kind. | |
2832 */ | |
2833 DartObjectImpl times(TypeProvider typeProvider, DartObjectImpl rightOperand) { | |
2834 InstanceState result = _state.times(rightOperand._state); | |
2835 if (result is IntState) { | |
2836 return new DartObjectImpl(typeProvider.intType, result); | |
2837 } else if (result is DoubleState) { | |
2838 return new DartObjectImpl(typeProvider.doubleType, result); | |
2839 } else if (result is NumState) { | |
2840 return new DartObjectImpl(typeProvider.numType, result); | |
2841 } | |
2842 // We should never get here. | |
2843 throw new IllegalStateException("times returned a ${result.runtimeType}"); | |
2844 } | |
2845 | |
2846 @override | |
2847 String toString() => "${type.displayName} ($_state)"; | |
2848 } | |
2849 | |
2850 /** | |
2851 * An object used to provide access to the values of variables that have been | |
2852 * defined on the command line using the `-D` option. | |
2853 */ | |
2854 class DeclaredVariables { | |
2855 /** | |
2856 * A table mapping the names of declared variables to their values. | |
2857 */ | |
2858 HashMap<String, String> _declaredVariables = new HashMap<String, String>(); | |
2859 | |
2860 /** | |
2861 * Define a variable with the given [name] to have the given [value]. | |
2862 */ | |
2863 void define(String name, String value) { | |
2864 _declaredVariables[name] = value; | |
2865 } | |
2866 | |
2867 /** | |
2868 * Return the value of the variable with the given [name] interpreted as a | |
2869 * 'boolean' value. If the variable is not defined (or [name] is `null`), a | |
2870 * DartObject representing "unknown" is returned. If the value cannot be | |
2871 * parsed as a boolean, a DartObject representing 'null' is returned. The | |
2872 * [typeProvider] is the type provider used to find the type 'bool'. | |
2873 */ | |
2874 DartObject getBool(TypeProvider typeProvider, String name) { | |
2875 String value = _declaredVariables[name]; | |
2876 if (value == null) { | |
2877 return new DartObjectImpl(typeProvider.boolType, BoolState.UNKNOWN_VALUE); | |
2878 } | |
2879 if (value == "true") { | |
2880 return new DartObjectImpl(typeProvider.boolType, BoolState.TRUE_STATE); | |
2881 } else if (value == "false") { | |
2882 return new DartObjectImpl(typeProvider.boolType, BoolState.FALSE_STATE); | |
2883 } | |
2884 return new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE); | |
2885 } | |
2886 | |
2887 /** | |
2888 * Return the value of the variable with the given [name] interpreted as an | |
2889 * integer value. If the variable is not defined (or [name] is `null`), a | |
2890 * DartObject representing "unknown" is returned. If the value cannot be | |
2891 * parsed as an integer, a DartObject representing 'null' is returned. | |
2892 */ | |
2893 DartObject getInt(TypeProvider typeProvider, String name) { | |
2894 String value = _declaredVariables[name]; | |
2895 if (value == null) { | |
2896 return new DartObjectImpl(typeProvider.intType, IntState.UNKNOWN_VALUE); | |
2897 } | |
2898 int bigInteger; | |
2899 try { | |
2900 bigInteger = int.parse(value); | |
2901 } on FormatException { | |
2902 return new DartObjectImpl(typeProvider.nullType, NullState.NULL_STATE); | |
2903 } | |
2904 return new DartObjectImpl(typeProvider.intType, new IntState(bigInteger)); | |
2905 } | |
2906 | |
2907 /** | |
2908 * Return the value of the variable with the given [name] interpreted as a | |
2909 * String value, or `null` if the variable is not defined. Return the value of | |
2910 * the variable with the given name interpreted as a String value. If the | |
2911 * variable is not defined (or [name] is `null`), a DartObject representing | |
2912 * "unknown" is returned. The [typeProvider] is the type provider used to find | |
2913 * the type 'String'. | |
2914 */ | |
2915 DartObject getString(TypeProvider typeProvider, String name) { | |
2916 String value = _declaredVariables[name]; | |
2917 if (value == null) { | |
2918 return new DartObjectImpl( | |
2919 typeProvider.stringType, StringState.UNKNOWN_VALUE); | |
2920 } | |
2921 return new DartObjectImpl(typeProvider.stringType, new StringState(value)); | |
2922 } | |
2923 } | |
2924 | |
2925 /** | |
2926 * The state of an object representing a double. | |
2927 */ | |
2928 class DoubleState extends NumState { | |
2929 /** | |
2930 * A state that can be used to represent a double whose value is not known. | |
2931 */ | |
2932 static DoubleState UNKNOWN_VALUE = new DoubleState(null); | |
2933 | |
2934 /** | |
2935 * The value of this instance. | |
2936 */ | |
2937 final double value; | |
2938 | |
2939 /** | |
2940 * Initialize a newly created state to represent a double with the given | |
2941 * [value]. | |
2942 */ | |
2943 DoubleState(this.value); | |
2944 | |
2945 @override | |
2946 bool get hasExactValue => true; | |
2947 | |
2948 @override | |
2949 int get hashCode => value == null ? 0 : value.hashCode; | |
2950 | |
2951 @override | |
2952 bool get isBoolNumStringOrNull => true; | |
2953 | |
2954 @override | |
2955 bool get isUnknown => value == null; | |
2956 | |
2957 @override | |
2958 String get typeName => "double"; | |
2959 | |
2960 @override | |
2961 bool operator ==(Object object) => | |
2962 object is DoubleState && (value == object.value); | |
2963 | |
2964 @override | |
2965 NumState add(InstanceState rightOperand) { | |
2966 assertNumOrNull(rightOperand); | |
2967 if (value == null) { | |
2968 return UNKNOWN_VALUE; | |
2969 } | |
2970 if (rightOperand is IntState) { | |
2971 int rightValue = rightOperand.value; | |
2972 if (rightValue == null) { | |
2973 return UNKNOWN_VALUE; | |
2974 } | |
2975 return new DoubleState(value + rightValue.toDouble()); | |
2976 } else if (rightOperand is DoubleState) { | |
2977 double rightValue = rightOperand.value; | |
2978 if (rightValue == null) { | |
2979 return UNKNOWN_VALUE; | |
2980 } | |
2981 return new DoubleState(value + rightValue); | |
2982 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
2983 return UNKNOWN_VALUE; | |
2984 } | |
2985 throw new EvaluationException( | |
2986 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
2987 } | |
2988 | |
2989 @override | |
2990 StringState convertToString() { | |
2991 if (value == null) { | |
2992 return StringState.UNKNOWN_VALUE; | |
2993 } | |
2994 return new StringState(value.toString()); | |
2995 } | |
2996 | |
2997 @override | |
2998 NumState divide(InstanceState rightOperand) { | |
2999 assertNumOrNull(rightOperand); | |
3000 if (value == null) { | |
3001 return UNKNOWN_VALUE; | |
3002 } | |
3003 if (rightOperand is IntState) { | |
3004 int rightValue = rightOperand.value; | |
3005 if (rightValue == null) { | |
3006 return UNKNOWN_VALUE; | |
3007 } | |
3008 return new DoubleState(value / rightValue.toDouble()); | |
3009 } else if (rightOperand is DoubleState) { | |
3010 double rightValue = rightOperand.value; | |
3011 if (rightValue == null) { | |
3012 return UNKNOWN_VALUE; | |
3013 } | |
3014 return new DoubleState(value / rightValue); | |
3015 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3016 return UNKNOWN_VALUE; | |
3017 } | |
3018 throw new EvaluationException( | |
3019 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
3020 } | |
3021 | |
3022 @override | |
3023 BoolState equalEqual(InstanceState rightOperand) { | |
3024 assertBoolNumStringOrNull(rightOperand); | |
3025 return isIdentical(rightOperand); | |
3026 } | |
3027 | |
3028 @override | |
3029 BoolState greaterThan(InstanceState rightOperand) { | |
3030 assertNumOrNull(rightOperand); | |
3031 if (value == null) { | |
3032 return BoolState.UNKNOWN_VALUE; | |
3033 } | |
3034 if (rightOperand is IntState) { | |
3035 int rightValue = rightOperand.value; | |
3036 if (rightValue == null) { | |
3037 return BoolState.UNKNOWN_VALUE; | |
3038 } | |
3039 return BoolState.from(value > rightValue.toDouble()); | |
3040 } else if (rightOperand is DoubleState) { | |
3041 double rightValue = rightOperand.value; | |
3042 if (rightValue == null) { | |
3043 return BoolState.UNKNOWN_VALUE; | |
3044 } | |
3045 return BoolState.from(value > rightValue); | |
3046 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3047 return BoolState.UNKNOWN_VALUE; | |
3048 } | |
3049 throw new EvaluationException( | |
3050 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
3051 } | |
3052 | |
3053 @override | |
3054 BoolState greaterThanOrEqual(InstanceState rightOperand) { | |
3055 assertNumOrNull(rightOperand); | |
3056 if (value == null) { | |
3057 return BoolState.UNKNOWN_VALUE; | |
3058 } | |
3059 if (rightOperand is IntState) { | |
3060 int rightValue = rightOperand.value; | |
3061 if (rightValue == null) { | |
3062 return BoolState.UNKNOWN_VALUE; | |
3063 } | |
3064 return BoolState.from(value >= rightValue.toDouble()); | |
3065 } else if (rightOperand is DoubleState) { | |
3066 double rightValue = rightOperand.value; | |
3067 if (rightValue == null) { | |
3068 return BoolState.UNKNOWN_VALUE; | |
3069 } | |
3070 return BoolState.from(value >= rightValue); | |
3071 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3072 return BoolState.UNKNOWN_VALUE; | |
3073 } | |
3074 throw new EvaluationException( | |
3075 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
3076 } | |
3077 | |
3078 @override | |
3079 IntState integerDivide(InstanceState rightOperand) { | |
3080 assertNumOrNull(rightOperand); | |
3081 if (value == null) { | |
3082 return IntState.UNKNOWN_VALUE; | |
3083 } | |
3084 if (rightOperand is IntState) { | |
3085 int rightValue = rightOperand.value; | |
3086 if (rightValue == null) { | |
3087 return IntState.UNKNOWN_VALUE; | |
3088 } | |
3089 double result = value / rightValue.toDouble(); | |
3090 return new IntState(result.toInt()); | |
3091 } else if (rightOperand is DoubleState) { | |
3092 double rightValue = rightOperand.value; | |
3093 if (rightValue == null) { | |
3094 return IntState.UNKNOWN_VALUE; | |
3095 } | |
3096 double result = value / rightValue; | |
3097 return new IntState(result.toInt()); | |
3098 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3099 return IntState.UNKNOWN_VALUE; | |
3100 } | |
3101 throw new EvaluationException( | |
3102 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
3103 } | |
3104 | |
3105 @override | |
3106 BoolState isIdentical(InstanceState rightOperand) { | |
3107 if (value == null) { | |
3108 return BoolState.UNKNOWN_VALUE; | |
3109 } | |
3110 if (rightOperand is DoubleState) { | |
3111 double rightValue = rightOperand.value; | |
3112 if (rightValue == null) { | |
3113 return BoolState.UNKNOWN_VALUE; | |
3114 } | |
3115 return BoolState.from(value == rightValue); | |
3116 } else if (rightOperand is IntState) { | |
3117 int rightValue = rightOperand.value; | |
3118 if (rightValue == null) { | |
3119 return BoolState.UNKNOWN_VALUE; | |
3120 } | |
3121 return BoolState.from(value == rightValue.toDouble()); | |
3122 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3123 return BoolState.UNKNOWN_VALUE; | |
3124 } | |
3125 return BoolState.FALSE_STATE; | |
3126 } | |
3127 | |
3128 @override | |
3129 BoolState lessThan(InstanceState rightOperand) { | |
3130 assertNumOrNull(rightOperand); | |
3131 if (value == null) { | |
3132 return BoolState.UNKNOWN_VALUE; | |
3133 } | |
3134 if (rightOperand is IntState) { | |
3135 int rightValue = rightOperand.value; | |
3136 if (rightValue == null) { | |
3137 return BoolState.UNKNOWN_VALUE; | |
3138 } | |
3139 return BoolState.from(value < rightValue.toDouble()); | |
3140 } else if (rightOperand is DoubleState) { | |
3141 double rightValue = rightOperand.value; | |
3142 if (rightValue == null) { | |
3143 return BoolState.UNKNOWN_VALUE; | |
3144 } | |
3145 return BoolState.from(value < rightValue); | |
3146 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3147 return BoolState.UNKNOWN_VALUE; | |
3148 } | |
3149 throw new EvaluationException( | |
3150 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
3151 } | |
3152 | |
3153 @override | |
3154 BoolState lessThanOrEqual(InstanceState rightOperand) { | |
3155 assertNumOrNull(rightOperand); | |
3156 if (value == null) { | |
3157 return BoolState.UNKNOWN_VALUE; | |
3158 } | |
3159 if (rightOperand is IntState) { | |
3160 int rightValue = rightOperand.value; | |
3161 if (rightValue == null) { | |
3162 return BoolState.UNKNOWN_VALUE; | |
3163 } | |
3164 return BoolState.from(value <= rightValue.toDouble()); | |
3165 } else if (rightOperand is DoubleState) { | |
3166 double rightValue = rightOperand.value; | |
3167 if (rightValue == null) { | |
3168 return BoolState.UNKNOWN_VALUE; | |
3169 } | |
3170 return BoolState.from(value <= rightValue); | |
3171 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3172 return BoolState.UNKNOWN_VALUE; | |
3173 } | |
3174 throw new EvaluationException( | |
3175 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
3176 } | |
3177 | |
3178 @override | |
3179 NumState minus(InstanceState rightOperand) { | |
3180 assertNumOrNull(rightOperand); | |
3181 if (value == null) { | |
3182 return UNKNOWN_VALUE; | |
3183 } | |
3184 if (rightOperand is IntState) { | |
3185 int rightValue = rightOperand.value; | |
3186 if (rightValue == null) { | |
3187 return UNKNOWN_VALUE; | |
3188 } | |
3189 return new DoubleState(value - rightValue.toDouble()); | |
3190 } else if (rightOperand is DoubleState) { | |
3191 double rightValue = rightOperand.value; | |
3192 if (rightValue == null) { | |
3193 return UNKNOWN_VALUE; | |
3194 } | |
3195 return new DoubleState(value - rightValue); | |
3196 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3197 return UNKNOWN_VALUE; | |
3198 } | |
3199 throw new EvaluationException( | |
3200 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
3201 } | |
3202 | |
3203 @override | |
3204 NumState negated() { | |
3205 if (value == null) { | |
3206 return UNKNOWN_VALUE; | |
3207 } | |
3208 return new DoubleState(-(value)); | |
3209 } | |
3210 | |
3211 @override | |
3212 NumState remainder(InstanceState rightOperand) { | |
3213 assertNumOrNull(rightOperand); | |
3214 if (value == null) { | |
3215 return UNKNOWN_VALUE; | |
3216 } | |
3217 if (rightOperand is IntState) { | |
3218 int rightValue = rightOperand.value; | |
3219 if (rightValue == null) { | |
3220 return UNKNOWN_VALUE; | |
3221 } | |
3222 return new DoubleState(value % rightValue.toDouble()); | |
3223 } else if (rightOperand is DoubleState) { | |
3224 double rightValue = rightOperand.value; | |
3225 if (rightValue == null) { | |
3226 return UNKNOWN_VALUE; | |
3227 } | |
3228 return new DoubleState(value % rightValue); | |
3229 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3230 return UNKNOWN_VALUE; | |
3231 } | |
3232 throw new EvaluationException( | |
3233 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
3234 } | |
3235 | |
3236 @override | |
3237 NumState times(InstanceState rightOperand) { | |
3238 assertNumOrNull(rightOperand); | |
3239 if (value == null) { | |
3240 return UNKNOWN_VALUE; | |
3241 } | |
3242 if (rightOperand is IntState) { | |
3243 int rightValue = rightOperand.value; | |
3244 if (rightValue == null) { | |
3245 return UNKNOWN_VALUE; | |
3246 } | |
3247 return new DoubleState(value * rightValue.toDouble()); | |
3248 } else if (rightOperand is DoubleState) { | |
3249 double rightValue = rightOperand.value; | |
3250 if (rightValue == null) { | |
3251 return UNKNOWN_VALUE; | |
3252 } | |
3253 return new DoubleState(value * rightValue); | |
3254 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
3255 return UNKNOWN_VALUE; | |
3256 } | |
3257 throw new EvaluationException( | |
3258 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
3259 } | |
3260 | |
3261 @override | |
3262 String toString() => value == null ? "-unknown-" : value.toString(); | |
3263 } | |
3264 | |
3265 /** | |
3266 * The state of an object representing a Dart object for which there is no type | |
3267 * information. | |
3268 */ | |
3269 class DynamicState extends InstanceState { | |
3270 /** | |
3271 * The unique instance of this class. | |
3272 */ | |
3273 static DynamicState DYNAMIC_STATE = new DynamicState(); | |
3274 | |
3275 @override | |
3276 bool get isBool => true; | |
3277 | |
3278 @override | |
3279 bool get isBoolNumStringOrNull => true; | |
3280 | |
3281 @override | |
3282 String get typeName => "dynamic"; | |
3283 | |
3284 @override | |
3285 NumState add(InstanceState rightOperand) { | |
3286 assertNumOrNull(rightOperand); | |
3287 return _unknownNum(rightOperand); | |
3288 } | |
3289 | |
3290 @override | |
3291 IntState bitAnd(InstanceState rightOperand) { | |
3292 assertIntOrNull(rightOperand); | |
3293 return IntState.UNKNOWN_VALUE; | |
3294 } | |
3295 | |
3296 @override | |
3297 IntState bitNot() => IntState.UNKNOWN_VALUE; | |
3298 | |
3299 @override | |
3300 IntState bitOr(InstanceState rightOperand) { | |
3301 assertIntOrNull(rightOperand); | |
3302 return IntState.UNKNOWN_VALUE; | |
3303 } | |
3304 | |
3305 @override | |
3306 IntState bitXor(InstanceState rightOperand) { | |
3307 assertIntOrNull(rightOperand); | |
3308 return IntState.UNKNOWN_VALUE; | |
3309 } | |
3310 | |
3311 @override | |
3312 StringState concatenate(InstanceState rightOperand) { | |
3313 assertString(rightOperand); | |
3314 return StringState.UNKNOWN_VALUE; | |
3315 } | |
3316 | |
3317 @override | |
3318 BoolState convertToBool() => BoolState.UNKNOWN_VALUE; | |
3319 | |
3320 @override | |
3321 StringState convertToString() => StringState.UNKNOWN_VALUE; | |
3322 | |
3323 @override | |
3324 NumState divide(InstanceState rightOperand) { | |
3325 assertNumOrNull(rightOperand); | |
3326 return _unknownNum(rightOperand); | |
3327 } | |
3328 | |
3329 @override | |
3330 BoolState equalEqual(InstanceState rightOperand) { | |
3331 assertBoolNumStringOrNull(rightOperand); | |
3332 return BoolState.UNKNOWN_VALUE; | |
3333 } | |
3334 | |
3335 @override | |
3336 BoolState greaterThan(InstanceState rightOperand) { | |
3337 assertNumOrNull(rightOperand); | |
3338 return BoolState.UNKNOWN_VALUE; | |
3339 } | |
3340 | |
3341 @override | |
3342 BoolState greaterThanOrEqual(InstanceState rightOperand) { | |
3343 assertNumOrNull(rightOperand); | |
3344 return BoolState.UNKNOWN_VALUE; | |
3345 } | |
3346 | |
3347 @override | |
3348 IntState integerDivide(InstanceState rightOperand) { | |
3349 assertNumOrNull(rightOperand); | |
3350 return IntState.UNKNOWN_VALUE; | |
3351 } | |
3352 | |
3353 @override | |
3354 BoolState isIdentical(InstanceState rightOperand) { | |
3355 return BoolState.UNKNOWN_VALUE; | |
3356 } | |
3357 | |
3358 @override | |
3359 BoolState lessThan(InstanceState rightOperand) { | |
3360 assertNumOrNull(rightOperand); | |
3361 return BoolState.UNKNOWN_VALUE; | |
3362 } | |
3363 | |
3364 @override | |
3365 BoolState lessThanOrEqual(InstanceState rightOperand) { | |
3366 assertNumOrNull(rightOperand); | |
3367 return BoolState.UNKNOWN_VALUE; | |
3368 } | |
3369 | |
3370 @override | |
3371 BoolState logicalAnd(InstanceState rightOperand) { | |
3372 assertBool(rightOperand); | |
3373 return BoolState.UNKNOWN_VALUE; | |
3374 } | |
3375 | |
3376 @override | |
3377 BoolState logicalNot() => BoolState.UNKNOWN_VALUE; | |
3378 | |
3379 @override | |
3380 BoolState logicalOr(InstanceState rightOperand) { | |
3381 assertBool(rightOperand); | |
3382 return rightOperand.convertToBool(); | |
3383 } | |
3384 | |
3385 @override | |
3386 NumState minus(InstanceState rightOperand) { | |
3387 assertNumOrNull(rightOperand); | |
3388 return _unknownNum(rightOperand); | |
3389 } | |
3390 | |
3391 @override | |
3392 NumState negated() => NumState.UNKNOWN_VALUE; | |
3393 | |
3394 @override | |
3395 NumState remainder(InstanceState rightOperand) { | |
3396 assertNumOrNull(rightOperand); | |
3397 return _unknownNum(rightOperand); | |
3398 } | |
3399 | |
3400 @override | |
3401 IntState shiftLeft(InstanceState rightOperand) { | |
3402 assertIntOrNull(rightOperand); | |
3403 return IntState.UNKNOWN_VALUE; | |
3404 } | |
3405 | |
3406 @override | |
3407 IntState shiftRight(InstanceState rightOperand) { | |
3408 assertIntOrNull(rightOperand); | |
3409 return IntState.UNKNOWN_VALUE; | |
3410 } | |
3411 | |
3412 @override | |
3413 NumState times(InstanceState rightOperand) { | |
3414 assertNumOrNull(rightOperand); | |
3415 return _unknownNum(rightOperand); | |
3416 } | |
3417 | |
3418 /** | |
3419 * Return an object representing an unknown numeric value whose type is based | |
3420 * on the type of the [rightOperand]. | |
3421 */ | |
3422 NumState _unknownNum(InstanceState rightOperand) { | |
3423 if (rightOperand is IntState) { | |
3424 return IntState.UNKNOWN_VALUE; | |
3425 } else if (rightOperand is DoubleState) { | |
3426 return DoubleState.UNKNOWN_VALUE; | |
3427 } | |
3428 return NumState.UNKNOWN_VALUE; | |
3429 } | |
3430 } | |
3431 | |
3432 /** | |
3433 * A run-time exception that would be thrown during the evaluation of Dart code. | |
3434 */ | |
3435 class EvaluationException extends JavaException { | |
3436 /** | |
3437 * The error code associated with the exception. | |
3438 */ | |
3439 final ErrorCode errorCode; | |
3440 | |
3441 /** | |
3442 * Initialize a newly created exception to have the given [errorCode]. | |
3443 */ | |
3444 EvaluationException(this.errorCode); | |
3445 } | |
3446 | |
3447 /** | |
3448 * The result of attempting to evaluate an expression. | |
3449 */ | |
3450 class EvaluationResult { | |
3451 /** | |
3452 * The value of the expression. | |
3453 */ | |
3454 final DartObject value; | |
3455 | |
3456 /** | |
3457 * The errors that should be reported for the expression(s) that were | |
3458 * evaluated. | |
3459 */ | |
3460 final List<AnalysisError> _errors; | |
3461 | |
3462 /** | |
3463 * Initialize a newly created result object with the given [value] and set of | |
3464 * [_errors]. Clients should use one of the factory methods: [forErrors] and | |
3465 * [forValue]. | |
3466 */ | |
3467 EvaluationResult(this.value, this._errors); | |
3468 | |
3469 /** | |
3470 * Return a list containing the errors that should be reported for the | |
3471 * expression(s) that were evaluated. If there are no such errors, the list | |
3472 * will be empty. The list can be empty even if the expression is not a valid | |
3473 * compile time constant if the errors would have been reported by other parts | |
3474 * of the analysis engine. | |
3475 */ | |
3476 List<AnalysisError> get errors => | |
3477 _errors == null ? AnalysisError.NO_ERRORS : _errors; | |
3478 | |
3479 /** | |
3480 * Return `true` if the expression is a compile-time constant expression that | |
3481 * would not throw an exception when evaluated. | |
3482 */ | |
3483 bool get isValid => _errors == null; | |
3484 | |
3485 /** | |
3486 * Return an evaluation result representing the result of evaluating an | |
3487 * expression that is not a compile-time constant because of the given | |
3488 * [errors]. | |
3489 */ | |
3490 static EvaluationResult forErrors(List<AnalysisError> errors) => | |
3491 new EvaluationResult(null, errors); | |
3492 | |
3493 /** | |
3494 * Return an evaluation result representing the result of evaluating an | |
3495 * expression that is a compile-time constant that evaluates to the given | |
3496 * [value]. | |
3497 */ | |
3498 static EvaluationResult forValue(DartObject value) => | |
3499 new EvaluationResult(value, null); | |
3500 } | |
3501 | |
3502 /** | |
3503 * The result of attempting to evaluate a expression. | |
3504 */ | |
3505 class EvaluationResultImpl { | |
3506 /** | |
3507 * The errors encountered while trying to evaluate the compile time constant. | |
3508 * These errors may or may not have prevented the expression from being a | |
3509 * valid compile time constant. | |
3510 */ | |
3511 List<AnalysisError> _errors; | |
3512 | |
3513 /** | |
3514 * The value of the expression, or `null` if the value couldn't be computed | |
3515 * due to errors. | |
3516 */ | |
3517 final DartObjectImpl value; | |
3518 | |
3519 EvaluationResultImpl(this.value, [List<AnalysisError> errors]) { | |
3520 this._errors = errors == null ? <AnalysisError>[] : errors; | |
3521 } | |
3522 | |
3523 @deprecated // Use new EvaluationResultImpl(value) | |
3524 EvaluationResultImpl.con1(this.value) { | |
3525 this._errors = new List<AnalysisError>(0); | |
3526 } | |
3527 | |
3528 @deprecated // Use new EvaluationResultImpl(value, errors) | |
3529 EvaluationResultImpl.con2(this.value, List<AnalysisError> errors) { | |
3530 this._errors = errors; | |
3531 } | |
3532 | |
3533 List<AnalysisError> get errors => _errors; | |
3534 | |
3535 bool equalValues(TypeProvider typeProvider, EvaluationResultImpl result) { | |
3536 if (this.value != null) { | |
3537 if (result.value == null) { | |
3538 return false; | |
3539 } | |
3540 return value == result.value; | |
3541 } else { | |
3542 return false; | |
3543 } | |
3544 } | |
3545 | |
3546 @override | |
3547 String toString() { | |
3548 if (value == null) { | |
3549 return "error"; | |
3550 } | |
3551 return value.toString(); | |
3552 } | |
3553 } | |
3554 | |
3555 /** | |
3556 * The state of an object representing a function. | |
3557 */ | |
3558 class FunctionState extends InstanceState { | |
3559 /** | |
3560 * The element representing the function being modeled. | |
3561 */ | |
3562 final ExecutableElement _element; | |
3563 | |
3564 /** | |
3565 * Initialize a newly created state to represent the function with the given | |
3566 * [element]. | |
3567 */ | |
3568 FunctionState(this._element); | |
3569 | |
3570 @override | |
3571 int get hashCode => _element == null ? 0 : _element.hashCode; | |
3572 | |
3573 @override | |
3574 String get typeName => "Function"; | |
3575 | |
3576 @override | |
3577 bool operator ==(Object object) => | |
3578 object is FunctionState && (_element == object._element); | |
3579 | |
3580 @override | |
3581 StringState convertToString() { | |
3582 if (_element == null) { | |
3583 return StringState.UNKNOWN_VALUE; | |
3584 } | |
3585 return new StringState(_element.name); | |
3586 } | |
3587 | |
3588 @override | |
3589 BoolState equalEqual(InstanceState rightOperand) { | |
3590 return isIdentical(rightOperand); | |
3591 } | |
3592 | |
3593 @override | |
3594 BoolState isIdentical(InstanceState rightOperand) { | |
3595 if (_element == null) { | |
3596 return BoolState.UNKNOWN_VALUE; | |
3597 } | |
3598 if (rightOperand is FunctionState) { | |
3599 ExecutableElement rightElement = rightOperand._element; | |
3600 if (rightElement == null) { | |
3601 return BoolState.UNKNOWN_VALUE; | |
3602 } | |
3603 return BoolState.from(_element == rightElement); | |
3604 } else if (rightOperand is DynamicState) { | |
3605 return BoolState.UNKNOWN_VALUE; | |
3606 } | |
3607 return BoolState.FALSE_STATE; | |
3608 } | |
3609 | |
3610 @override | |
3611 String toString() => _element == null ? "-unknown-" : _element.name; | |
3612 } | |
3613 | |
3614 /** | |
3615 * The state of an object representing a Dart object for which there is no more | |
3616 * specific state. | |
3617 */ | |
3618 class GenericState extends InstanceState { | |
3619 /** | |
3620 * Pseudo-field that we use to represent fields in the superclass. | |
3621 */ | |
3622 static String SUPERCLASS_FIELD = "(super)"; | |
3623 | |
3624 /** | |
3625 * A state that can be used to represent an object whose state is not known. | |
3626 */ | |
3627 static GenericState UNKNOWN_VALUE = | |
3628 new GenericState(new HashMap<String, DartObjectImpl>()); | |
3629 | |
3630 /** | |
3631 * The values of the fields of this instance. | |
3632 */ | |
3633 final HashMap<String, DartObjectImpl> _fieldMap; | |
3634 | |
3635 /** | |
3636 * Initialize a newly created state to represent a newly created object. The | |
3637 * [fieldMap] contains the values of the fields of the instance. | |
3638 */ | |
3639 GenericState(this._fieldMap); | |
3640 | |
3641 @override | |
3642 HashMap<String, DartObjectImpl> get fields => _fieldMap; | |
3643 | |
3644 @override | |
3645 int get hashCode { | |
3646 int hashCode = 0; | |
3647 for (DartObjectImpl value in _fieldMap.values) { | |
3648 hashCode += value.hashCode; | |
3649 } | |
3650 return hashCode; | |
3651 } | |
3652 | |
3653 @override | |
3654 bool get isUnknown => identical(this, UNKNOWN_VALUE); | |
3655 | |
3656 @override | |
3657 String get typeName => "user defined type"; | |
3658 | |
3659 @override | |
3660 bool operator ==(Object object) { | |
3661 if (object is! GenericState) { | |
3662 return false; | |
3663 } | |
3664 GenericState state = object as GenericState; | |
3665 HashSet<String> otherFields = | |
3666 new HashSet<String>.from(state._fieldMap.keys.toSet()); | |
3667 for (String fieldName in _fieldMap.keys.toSet()) { | |
3668 if (_fieldMap[fieldName] != state._fieldMap[fieldName]) { | |
3669 return false; | |
3670 } | |
3671 otherFields.remove(fieldName); | |
3672 } | |
3673 for (String fieldName in otherFields) { | |
3674 if (state._fieldMap[fieldName] != _fieldMap[fieldName]) { | |
3675 return false; | |
3676 } | |
3677 } | |
3678 return true; | |
3679 } | |
3680 | |
3681 @override | |
3682 StringState convertToString() => StringState.UNKNOWN_VALUE; | |
3683 | |
3684 @override | |
3685 BoolState equalEqual(InstanceState rightOperand) { | |
3686 assertBoolNumStringOrNull(rightOperand); | |
3687 return isIdentical(rightOperand); | |
3688 } | |
3689 | |
3690 @override | |
3691 BoolState isIdentical(InstanceState rightOperand) { | |
3692 if (rightOperand is DynamicState) { | |
3693 return BoolState.UNKNOWN_VALUE; | |
3694 } | |
3695 return BoolState.from(this == rightOperand); | |
3696 } | |
3697 | |
3698 @override | |
3699 String toString() { | |
3700 StringBuffer buffer = new StringBuffer(); | |
3701 List<String> fieldNames = _fieldMap.keys.toList(); | |
3702 fieldNames.sort(); | |
3703 bool first = true; | |
3704 for (String fieldName in fieldNames) { | |
3705 if (first) { | |
3706 first = false; | |
3707 } else { | |
3708 buffer.write('; '); | |
3709 } | |
3710 buffer.write(fieldName); | |
3711 buffer.write(' = '); | |
3712 buffer.write(_fieldMap[fieldName]); | |
3713 } | |
3714 return buffer.toString(); | |
3715 } | |
3716 } | |
3717 | |
3718 /** | |
3719 * The state of an object representing a Dart object. | |
3720 */ | |
3721 abstract class InstanceState { | |
3722 /** | |
3723 * If this represents a generic dart object, return a map from its field names | |
3724 * to their values. Otherwise return null. | |
3725 */ | |
3726 HashMap<String, DartObjectImpl> get fields => null; | |
3727 | |
3728 /** | |
3729 * Return `true` if this object's value can be represented exactly. | |
3730 */ | |
3731 bool get hasExactValue => false; | |
3732 | |
3733 /** | |
3734 * Return `true` if this object represents an object whose type is 'bool'. | |
3735 */ | |
3736 bool get isBool => false; | |
3737 | |
3738 /** | |
3739 * Return `true` if this object represents an object whose type is either | |
3740 * 'bool', 'num', 'String', or 'Null'. | |
3741 */ | |
3742 bool get isBoolNumStringOrNull => false; | |
3743 | |
3744 /** | |
3745 * Return `true` if this object represents an unknown value. | |
3746 */ | |
3747 bool get isUnknown => false; | |
3748 | |
3749 /** | |
3750 * Return the name of the type of this value. | |
3751 */ | |
3752 String get typeName; | |
3753 | |
3754 /** | |
3755 * Return this object's value if it can be represented exactly, or `null` if | |
3756 * either the value cannot be represented exactly or if the value is `null`. | |
3757 * Clients should use [hasExactValue] to distinguish between these two cases. | |
3758 */ | |
3759 Object get value => null; | |
3760 | |
3761 /** | |
3762 * Return the result of invoking the '+' operator on this object with the | |
3763 * [rightOperand]. | |
3764 * | |
3765 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3766 * object of this kind. | |
3767 */ | |
3768 InstanceState add(InstanceState rightOperand) { | |
3769 if (this is StringState && rightOperand is StringState) { | |
3770 return concatenate(rightOperand); | |
3771 } | |
3772 assertNumOrNull(this); | |
3773 assertNumOrNull(rightOperand); | |
3774 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3775 } | |
3776 | |
3777 /** | |
3778 * Throw an exception if the given [state] does not represent a boolean value. | |
3779 */ | |
3780 void assertBool(InstanceState state) { | |
3781 if (!(state is BoolState || state is DynamicState)) { | |
3782 throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL); | |
3783 } | |
3784 } | |
3785 | |
3786 /** | |
3787 * Throw an exception if the given [state] does not represent a boolean, | |
3788 * numeric, string or null value. | |
3789 */ | |
3790 void assertBoolNumStringOrNull(InstanceState state) { | |
3791 if (!(state is BoolState || | |
3792 state is DoubleState || | |
3793 state is IntState || | |
3794 state is NumState || | |
3795 state is StringState || | |
3796 state is NullState || | |
3797 state is DynamicState)) { | |
3798 throw new EvaluationException( | |
3799 CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING); | |
3800 } | |
3801 } | |
3802 | |
3803 /** | |
3804 * Throw an exception if the given [state] does not represent an integer or | |
3805 * null value. | |
3806 */ | |
3807 void assertIntOrNull(InstanceState state) { | |
3808 if (!(state is IntState || | |
3809 state is NumState || | |
3810 state is NullState || | |
3811 state is DynamicState)) { | |
3812 throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_INT); | |
3813 } | |
3814 } | |
3815 | |
3816 /** | |
3817 * Throw an exception if the given [state] does not represent a boolean, | |
3818 * numeric, string or null value. | |
3819 */ | |
3820 void assertNumOrNull(InstanceState state) { | |
3821 if (!(state is DoubleState || | |
3822 state is IntState || | |
3823 state is NumState || | |
3824 state is NullState || | |
3825 state is DynamicState)) { | |
3826 throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_NUM); | |
3827 } | |
3828 } | |
3829 | |
3830 /** | |
3831 * Throw an exception if the given [state] does not represent a String value. | |
3832 */ | |
3833 void assertString(InstanceState state) { | |
3834 if (!(state is StringState || state is DynamicState)) { | |
3835 throw new EvaluationException(CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL); | |
3836 } | |
3837 } | |
3838 | |
3839 /** | |
3840 * Return the result of invoking the '&' operator on this object with the | |
3841 * [rightOperand]. | |
3842 * | |
3843 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3844 * object of this kind. | |
3845 */ | |
3846 IntState bitAnd(InstanceState rightOperand) { | |
3847 assertIntOrNull(this); | |
3848 assertIntOrNull(rightOperand); | |
3849 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3850 } | |
3851 | |
3852 /** | |
3853 * Return the result of invoking the '~' operator on this object. | |
3854 * | |
3855 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3856 * object of this kind. | |
3857 */ | |
3858 IntState bitNot() { | |
3859 assertIntOrNull(this); | |
3860 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3861 } | |
3862 | |
3863 /** | |
3864 * Return the result of invoking the '|' operator on this object with the | |
3865 * [rightOperand]. | |
3866 * | |
3867 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3868 * object of this kind. | |
3869 */ | |
3870 IntState bitOr(InstanceState rightOperand) { | |
3871 assertIntOrNull(this); | |
3872 assertIntOrNull(rightOperand); | |
3873 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3874 } | |
3875 | |
3876 /** | |
3877 * Return the result of invoking the '^' operator on this object with the | |
3878 * [rightOperand]. | |
3879 * | |
3880 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3881 * object of this kind. | |
3882 */ | |
3883 IntState bitXor(InstanceState rightOperand) { | |
3884 assertIntOrNull(this); | |
3885 assertIntOrNull(rightOperand); | |
3886 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3887 } | |
3888 | |
3889 /** | |
3890 * Return the result of invoking the ' ' operator on this object with the | |
3891 * [rightOperand]. | |
3892 * | |
3893 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3894 * object of this kind. | |
3895 */ | |
3896 StringState concatenate(InstanceState rightOperand) { | |
3897 assertString(rightOperand); | |
3898 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3899 } | |
3900 | |
3901 /** | |
3902 * Return the result of applying boolean conversion to this object. | |
3903 * | |
3904 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3905 * object of this kind. | |
3906 */ | |
3907 BoolState convertToBool() => BoolState.FALSE_STATE; | |
3908 | |
3909 /** | |
3910 * Return the result of converting this object to a String. | |
3911 * | |
3912 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3913 * object of this kind. | |
3914 */ | |
3915 StringState convertToString(); | |
3916 | |
3917 /** | |
3918 * Return the result of invoking the '/' operator on this object with the | |
3919 * [rightOperand]. | |
3920 * | |
3921 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3922 * object of this kind. | |
3923 */ | |
3924 NumState divide(InstanceState rightOperand) { | |
3925 assertNumOrNull(this); | |
3926 assertNumOrNull(rightOperand); | |
3927 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3928 } | |
3929 | |
3930 /** | |
3931 * Return the result of invoking the '==' operator on this object with the | |
3932 * [rightOperand]. | |
3933 * | |
3934 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3935 * object of this kind. | |
3936 */ | |
3937 BoolState equalEqual(InstanceState rightOperand); | |
3938 | |
3939 /** | |
3940 * Return the result of invoking the '>' operator on this object with the | |
3941 * [rightOperand]. | |
3942 * | |
3943 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3944 * object of this kind. | |
3945 */ | |
3946 BoolState greaterThan(InstanceState rightOperand) { | |
3947 assertNumOrNull(this); | |
3948 assertNumOrNull(rightOperand); | |
3949 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3950 } | |
3951 | |
3952 /** | |
3953 * Return the result of invoking the '>=' operator on this object with the | |
3954 * [rightOperand]. | |
3955 * | |
3956 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3957 * object of this kind. | |
3958 */ | |
3959 BoolState greaterThanOrEqual(InstanceState rightOperand) { | |
3960 assertNumOrNull(this); | |
3961 assertNumOrNull(rightOperand); | |
3962 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3963 } | |
3964 | |
3965 /** | |
3966 * Return the result of invoking the '~/' operator on this object with the | |
3967 * [rightOperand]. | |
3968 * | |
3969 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3970 * object of this kind. | |
3971 */ | |
3972 IntState integerDivide(InstanceState rightOperand) { | |
3973 assertNumOrNull(this); | |
3974 assertNumOrNull(rightOperand); | |
3975 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3976 } | |
3977 | |
3978 /** | |
3979 * Return the result of invoking the identical function on this object with | |
3980 * the [rightOperand]. | |
3981 */ | |
3982 BoolState isIdentical(InstanceState rightOperand); | |
3983 | |
3984 /** | |
3985 * Return the result of invoking the '<' operator on this object with the | |
3986 * [rightOperand]. | |
3987 * | |
3988 * Throws an [EvaluationException] if the operator is not appropriate for an | |
3989 * object of this kind. | |
3990 */ | |
3991 BoolState lessThan(InstanceState rightOperand) { | |
3992 assertNumOrNull(this); | |
3993 assertNumOrNull(rightOperand); | |
3994 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
3995 } | |
3996 | |
3997 /** | |
3998 * Return the result of invoking the '<=' operator on this object with the | |
3999 * [rightOperand]. | |
4000 * | |
4001 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4002 * object of this kind. | |
4003 */ | |
4004 BoolState lessThanOrEqual(InstanceState rightOperand) { | |
4005 assertNumOrNull(this); | |
4006 assertNumOrNull(rightOperand); | |
4007 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
4008 } | |
4009 | |
4010 /** | |
4011 * Return the result of invoking the '&&' operator on this object with the | |
4012 * [rightOperand]. | |
4013 * | |
4014 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4015 * object of this kind. | |
4016 */ | |
4017 BoolState logicalAnd(InstanceState rightOperand) { | |
4018 assertBool(this); | |
4019 assertBool(rightOperand); | |
4020 return BoolState.FALSE_STATE; | |
4021 } | |
4022 | |
4023 /** | |
4024 * Return the result of invoking the '!' operator on this object. | |
4025 * | |
4026 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4027 * object of this kind. | |
4028 */ | |
4029 BoolState logicalNot() { | |
4030 assertBool(this); | |
4031 return BoolState.TRUE_STATE; | |
4032 } | |
4033 | |
4034 /** | |
4035 * Return the result of invoking the '||' operator on this object with the | |
4036 * [rightOperand]. | |
4037 * | |
4038 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4039 * object of this kind. | |
4040 */ | |
4041 BoolState logicalOr(InstanceState rightOperand) { | |
4042 assertBool(this); | |
4043 assertBool(rightOperand); | |
4044 return rightOperand.convertToBool(); | |
4045 } | |
4046 | |
4047 /** | |
4048 * Return the result of invoking the '-' operator on this object with the | |
4049 * [rightOperand]. | |
4050 * | |
4051 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4052 * object of this kind. | |
4053 */ | |
4054 NumState minus(InstanceState rightOperand) { | |
4055 assertNumOrNull(this); | |
4056 assertNumOrNull(rightOperand); | |
4057 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
4058 } | |
4059 | |
4060 /** | |
4061 * Return the result of invoking the '-' operator on this object. | |
4062 * | |
4063 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4064 * object of this kind. | |
4065 */ | |
4066 NumState negated() { | |
4067 assertNumOrNull(this); | |
4068 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
4069 } | |
4070 | |
4071 /** | |
4072 * Return the result of invoking the '%' operator on this object with the | |
4073 * [rightOperand]. | |
4074 * | |
4075 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4076 * object of this kind. | |
4077 */ | |
4078 NumState remainder(InstanceState rightOperand) { | |
4079 assertNumOrNull(this); | |
4080 assertNumOrNull(rightOperand); | |
4081 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
4082 } | |
4083 | |
4084 /** | |
4085 * Return the result of invoking the '<<' operator on this object with | |
4086 * the [rightOperand]. | |
4087 * | |
4088 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4089 * object of this kind. | |
4090 */ | |
4091 IntState shiftLeft(InstanceState rightOperand) { | |
4092 assertIntOrNull(this); | |
4093 assertIntOrNull(rightOperand); | |
4094 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
4095 } | |
4096 | |
4097 /** | |
4098 * Return the result of invoking the '>>' operator on this object with | |
4099 * the [rightOperand]. | |
4100 * | |
4101 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4102 * object of this kind. | |
4103 */ | |
4104 IntState shiftRight(InstanceState rightOperand) { | |
4105 assertIntOrNull(this); | |
4106 assertIntOrNull(rightOperand); | |
4107 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
4108 } | |
4109 | |
4110 /** | |
4111 * Return the result of invoking the 'length' getter on this object. | |
4112 * | |
4113 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4114 * object of this kind. | |
4115 */ | |
4116 IntState stringLength() { | |
4117 assertString(this); | |
4118 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
4119 } | |
4120 | |
4121 /** | |
4122 * Return the result of invoking the '*' operator on this object with the | |
4123 * [rightOperand]. | |
4124 * | |
4125 * Throws an [EvaluationException] if the operator is not appropriate for an | |
4126 * object of this kind. | |
4127 */ | |
4128 NumState times(InstanceState rightOperand) { | |
4129 assertNumOrNull(this); | |
4130 assertNumOrNull(rightOperand); | |
4131 throw new EvaluationException(CompileTimeErrorCode.INVALID_CONSTANT); | |
4132 } | |
4133 } | |
4134 | |
4135 /** | |
4136 * The state of an object representing an int. | |
4137 */ | |
4138 class IntState extends NumState { | |
4139 /** | |
4140 * A state that can be used to represent an int whose value is not known. | |
4141 */ | |
4142 static IntState UNKNOWN_VALUE = new IntState(null); | |
4143 | |
4144 /** | |
4145 * The value of this instance. | |
4146 */ | |
4147 final int value; | |
4148 | |
4149 /** | |
4150 * Initialize a newly created state to represent an int with the given | |
4151 * [value]. | |
4152 */ | |
4153 IntState(this.value); | |
4154 | |
4155 @override | |
4156 bool get hasExactValue => true; | |
4157 | |
4158 @override | |
4159 int get hashCode => value == null ? 0 : value.hashCode; | |
4160 | |
4161 @override | |
4162 bool get isBoolNumStringOrNull => true; | |
4163 | |
4164 @override | |
4165 bool get isUnknown => value == null; | |
4166 | |
4167 @override | |
4168 String get typeName => "int"; | |
4169 | |
4170 @override | |
4171 bool operator ==(Object object) => | |
4172 object is IntState && (value == object.value); | |
4173 | |
4174 @override | |
4175 NumState add(InstanceState rightOperand) { | |
4176 assertNumOrNull(rightOperand); | |
4177 if (value == null) { | |
4178 if (rightOperand is DoubleState) { | |
4179 return DoubleState.UNKNOWN_VALUE; | |
4180 } | |
4181 return UNKNOWN_VALUE; | |
4182 } | |
4183 if (rightOperand is IntState) { | |
4184 int rightValue = rightOperand.value; | |
4185 if (rightValue == null) { | |
4186 return UNKNOWN_VALUE; | |
4187 } | |
4188 return new IntState(value + rightValue); | |
4189 } else if (rightOperand is DoubleState) { | |
4190 double rightValue = rightOperand.value; | |
4191 if (rightValue == null) { | |
4192 return DoubleState.UNKNOWN_VALUE; | |
4193 } | |
4194 return new DoubleState(value.toDouble() + rightValue); | |
4195 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4196 return UNKNOWN_VALUE; | |
4197 } | |
4198 throw new EvaluationException( | |
4199 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4200 } | |
4201 | |
4202 @override | |
4203 IntState bitAnd(InstanceState rightOperand) { | |
4204 assertIntOrNull(rightOperand); | |
4205 if (value == null) { | |
4206 return UNKNOWN_VALUE; | |
4207 } | |
4208 if (rightOperand is IntState) { | |
4209 int rightValue = rightOperand.value; | |
4210 if (rightValue == null) { | |
4211 return UNKNOWN_VALUE; | |
4212 } | |
4213 return new IntState(value & rightValue); | |
4214 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4215 return UNKNOWN_VALUE; | |
4216 } | |
4217 throw new EvaluationException( | |
4218 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4219 } | |
4220 | |
4221 @override | |
4222 IntState bitNot() { | |
4223 if (value == null) { | |
4224 return UNKNOWN_VALUE; | |
4225 } | |
4226 return new IntState(~value); | |
4227 } | |
4228 | |
4229 @override | |
4230 IntState bitOr(InstanceState rightOperand) { | |
4231 assertIntOrNull(rightOperand); | |
4232 if (value == null) { | |
4233 return UNKNOWN_VALUE; | |
4234 } | |
4235 if (rightOperand is IntState) { | |
4236 int rightValue = rightOperand.value; | |
4237 if (rightValue == null) { | |
4238 return UNKNOWN_VALUE; | |
4239 } | |
4240 return new IntState(value | rightValue); | |
4241 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4242 return UNKNOWN_VALUE; | |
4243 } | |
4244 throw new EvaluationException( | |
4245 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4246 } | |
4247 | |
4248 @override | |
4249 IntState bitXor(InstanceState rightOperand) { | |
4250 assertIntOrNull(rightOperand); | |
4251 if (value == null) { | |
4252 return UNKNOWN_VALUE; | |
4253 } | |
4254 if (rightOperand is IntState) { | |
4255 int rightValue = rightOperand.value; | |
4256 if (rightValue == null) { | |
4257 return UNKNOWN_VALUE; | |
4258 } | |
4259 return new IntState(value ^ rightValue); | |
4260 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4261 return UNKNOWN_VALUE; | |
4262 } | |
4263 throw new EvaluationException( | |
4264 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4265 } | |
4266 | |
4267 @override | |
4268 StringState convertToString() { | |
4269 if (value == null) { | |
4270 return StringState.UNKNOWN_VALUE; | |
4271 } | |
4272 return new StringState(value.toString()); | |
4273 } | |
4274 | |
4275 @override | |
4276 NumState divide(InstanceState rightOperand) { | |
4277 assertNumOrNull(rightOperand); | |
4278 if (value == null) { | |
4279 return DoubleState.UNKNOWN_VALUE; | |
4280 } | |
4281 if (rightOperand is IntState) { | |
4282 int rightValue = rightOperand.value; | |
4283 if (rightValue == null) { | |
4284 return DoubleState.UNKNOWN_VALUE; | |
4285 } else { | |
4286 return new DoubleState(value.toDouble() / rightValue.toDouble()); | |
4287 } | |
4288 } else if (rightOperand is DoubleState) { | |
4289 double rightValue = rightOperand.value; | |
4290 if (rightValue == null) { | |
4291 return DoubleState.UNKNOWN_VALUE; | |
4292 } | |
4293 return new DoubleState(value.toDouble() / rightValue); | |
4294 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4295 return DoubleState.UNKNOWN_VALUE; | |
4296 } | |
4297 throw new EvaluationException( | |
4298 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4299 } | |
4300 | |
4301 @override | |
4302 BoolState equalEqual(InstanceState rightOperand) { | |
4303 assertBoolNumStringOrNull(rightOperand); | |
4304 return isIdentical(rightOperand); | |
4305 } | |
4306 | |
4307 @override | |
4308 BoolState greaterThan(InstanceState rightOperand) { | |
4309 assertNumOrNull(rightOperand); | |
4310 if (value == null) { | |
4311 return BoolState.UNKNOWN_VALUE; | |
4312 } | |
4313 if (rightOperand is IntState) { | |
4314 int rightValue = rightOperand.value; | |
4315 if (rightValue == null) { | |
4316 return BoolState.UNKNOWN_VALUE; | |
4317 } | |
4318 return BoolState.from(value.compareTo(rightValue) > 0); | |
4319 } else if (rightOperand is DoubleState) { | |
4320 double rightValue = rightOperand.value; | |
4321 if (rightValue == null) { | |
4322 return BoolState.UNKNOWN_VALUE; | |
4323 } | |
4324 return BoolState.from(value.toDouble() > rightValue); | |
4325 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4326 return BoolState.UNKNOWN_VALUE; | |
4327 } | |
4328 throw new EvaluationException( | |
4329 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4330 } | |
4331 | |
4332 @override | |
4333 BoolState greaterThanOrEqual(InstanceState rightOperand) { | |
4334 assertNumOrNull(rightOperand); | |
4335 if (value == null) { | |
4336 return BoolState.UNKNOWN_VALUE; | |
4337 } | |
4338 if (rightOperand is IntState) { | |
4339 int rightValue = rightOperand.value; | |
4340 if (rightValue == null) { | |
4341 return BoolState.UNKNOWN_VALUE; | |
4342 } | |
4343 return BoolState.from(value.compareTo(rightValue) >= 0); | |
4344 } else if (rightOperand is DoubleState) { | |
4345 double rightValue = rightOperand.value; | |
4346 if (rightValue == null) { | |
4347 return BoolState.UNKNOWN_VALUE; | |
4348 } | |
4349 return BoolState.from(value.toDouble() >= rightValue); | |
4350 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4351 return BoolState.UNKNOWN_VALUE; | |
4352 } | |
4353 throw new EvaluationException( | |
4354 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4355 } | |
4356 | |
4357 @override | |
4358 IntState integerDivide(InstanceState rightOperand) { | |
4359 assertNumOrNull(rightOperand); | |
4360 if (value == null) { | |
4361 return UNKNOWN_VALUE; | |
4362 } | |
4363 if (rightOperand is IntState) { | |
4364 int rightValue = rightOperand.value; | |
4365 if (rightValue == null) { | |
4366 return UNKNOWN_VALUE; | |
4367 } else if (rightValue == 0) { | |
4368 throw new EvaluationException( | |
4369 CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE); | |
4370 } | |
4371 return new IntState(value ~/ rightValue); | |
4372 } else if (rightOperand is DoubleState) { | |
4373 double rightValue = rightOperand.value; | |
4374 if (rightValue == null) { | |
4375 return UNKNOWN_VALUE; | |
4376 } | |
4377 double result = value.toDouble() / rightValue; | |
4378 return new IntState(result.toInt()); | |
4379 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4380 return UNKNOWN_VALUE; | |
4381 } | |
4382 throw new EvaluationException( | |
4383 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4384 } | |
4385 | |
4386 @override | |
4387 BoolState isIdentical(InstanceState rightOperand) { | |
4388 if (value == null) { | |
4389 return BoolState.UNKNOWN_VALUE; | |
4390 } | |
4391 if (rightOperand is IntState) { | |
4392 int rightValue = rightOperand.value; | |
4393 if (rightValue == null) { | |
4394 return BoolState.UNKNOWN_VALUE; | |
4395 } | |
4396 return BoolState.from(value == rightValue); | |
4397 } else if (rightOperand is DoubleState) { | |
4398 double rightValue = rightOperand.value; | |
4399 if (rightValue == null) { | |
4400 return BoolState.UNKNOWN_VALUE; | |
4401 } | |
4402 return BoolState.from(rightValue == value.toDouble()); | |
4403 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4404 return BoolState.UNKNOWN_VALUE; | |
4405 } | |
4406 return BoolState.FALSE_STATE; | |
4407 } | |
4408 | |
4409 @override | |
4410 BoolState lessThan(InstanceState rightOperand) { | |
4411 assertNumOrNull(rightOperand); | |
4412 if (value == null) { | |
4413 return BoolState.UNKNOWN_VALUE; | |
4414 } | |
4415 if (rightOperand is IntState) { | |
4416 int rightValue = rightOperand.value; | |
4417 if (rightValue == null) { | |
4418 return BoolState.UNKNOWN_VALUE; | |
4419 } | |
4420 return BoolState.from(value.compareTo(rightValue) < 0); | |
4421 } else if (rightOperand is DoubleState) { | |
4422 double rightValue = rightOperand.value; | |
4423 if (rightValue == null) { | |
4424 return BoolState.UNKNOWN_VALUE; | |
4425 } | |
4426 return BoolState.from(value.toDouble() < rightValue); | |
4427 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4428 return BoolState.UNKNOWN_VALUE; | |
4429 } | |
4430 throw new EvaluationException( | |
4431 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4432 } | |
4433 | |
4434 @override | |
4435 BoolState lessThanOrEqual(InstanceState rightOperand) { | |
4436 assertNumOrNull(rightOperand); | |
4437 if (value == null) { | |
4438 return BoolState.UNKNOWN_VALUE; | |
4439 } | |
4440 if (rightOperand is IntState) { | |
4441 int rightValue = rightOperand.value; | |
4442 if (rightValue == null) { | |
4443 return BoolState.UNKNOWN_VALUE; | |
4444 } | |
4445 return BoolState.from(value.compareTo(rightValue) <= 0); | |
4446 } else if (rightOperand is DoubleState) { | |
4447 double rightValue = rightOperand.value; | |
4448 if (rightValue == null) { | |
4449 return BoolState.UNKNOWN_VALUE; | |
4450 } | |
4451 return BoolState.from(value.toDouble() <= rightValue); | |
4452 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4453 return BoolState.UNKNOWN_VALUE; | |
4454 } | |
4455 throw new EvaluationException( | |
4456 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4457 } | |
4458 | |
4459 @override | |
4460 NumState minus(InstanceState rightOperand) { | |
4461 assertNumOrNull(rightOperand); | |
4462 if (value == null) { | |
4463 if (rightOperand is DoubleState) { | |
4464 return DoubleState.UNKNOWN_VALUE; | |
4465 } | |
4466 return UNKNOWN_VALUE; | |
4467 } | |
4468 if (rightOperand is IntState) { | |
4469 int rightValue = rightOperand.value; | |
4470 if (rightValue == null) { | |
4471 return UNKNOWN_VALUE; | |
4472 } | |
4473 return new IntState(value - rightValue); | |
4474 } else if (rightOperand is DoubleState) { | |
4475 double rightValue = rightOperand.value; | |
4476 if (rightValue == null) { | |
4477 return DoubleState.UNKNOWN_VALUE; | |
4478 } | |
4479 return new DoubleState(value.toDouble() - rightValue); | |
4480 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4481 return UNKNOWN_VALUE; | |
4482 } | |
4483 throw new EvaluationException( | |
4484 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4485 } | |
4486 | |
4487 @override | |
4488 NumState negated() { | |
4489 if (value == null) { | |
4490 return UNKNOWN_VALUE; | |
4491 } | |
4492 return new IntState(-value); | |
4493 } | |
4494 | |
4495 @override | |
4496 NumState remainder(InstanceState rightOperand) { | |
4497 assertNumOrNull(rightOperand); | |
4498 if (value == null) { | |
4499 if (rightOperand is DoubleState) { | |
4500 return DoubleState.UNKNOWN_VALUE; | |
4501 } | |
4502 return UNKNOWN_VALUE; | |
4503 } | |
4504 if (rightOperand is IntState) { | |
4505 int rightValue = rightOperand.value; | |
4506 if (rightValue == null) { | |
4507 return UNKNOWN_VALUE; | |
4508 } else if (rightValue == 0) { | |
4509 return new DoubleState(value.toDouble() % rightValue.toDouble()); | |
4510 } | |
4511 return new IntState(value.remainder(rightValue)); | |
4512 } else if (rightOperand is DoubleState) { | |
4513 double rightValue = rightOperand.value; | |
4514 if (rightValue == null) { | |
4515 return DoubleState.UNKNOWN_VALUE; | |
4516 } | |
4517 return new DoubleState(value.toDouble() % rightValue); | |
4518 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4519 return UNKNOWN_VALUE; | |
4520 } | |
4521 throw new EvaluationException( | |
4522 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4523 } | |
4524 | |
4525 @override | |
4526 IntState shiftLeft(InstanceState rightOperand) { | |
4527 assertIntOrNull(rightOperand); | |
4528 if (value == null) { | |
4529 return UNKNOWN_VALUE; | |
4530 } | |
4531 if (rightOperand is IntState) { | |
4532 int rightValue = rightOperand.value; | |
4533 if (rightValue == null) { | |
4534 return UNKNOWN_VALUE; | |
4535 } else if (rightValue.bitLength > 31) { | |
4536 return UNKNOWN_VALUE; | |
4537 } | |
4538 return new IntState(value << rightValue); | |
4539 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4540 return UNKNOWN_VALUE; | |
4541 } | |
4542 throw new EvaluationException( | |
4543 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4544 } | |
4545 | |
4546 @override | |
4547 IntState shiftRight(InstanceState rightOperand) { | |
4548 assertIntOrNull(rightOperand); | |
4549 if (value == null) { | |
4550 return UNKNOWN_VALUE; | |
4551 } | |
4552 if (rightOperand is IntState) { | |
4553 int rightValue = rightOperand.value; | |
4554 if (rightValue == null) { | |
4555 return UNKNOWN_VALUE; | |
4556 } else if (rightValue.bitLength > 31) { | |
4557 return UNKNOWN_VALUE; | |
4558 } | |
4559 return new IntState(value >> rightValue); | |
4560 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4561 return UNKNOWN_VALUE; | |
4562 } | |
4563 throw new EvaluationException( | |
4564 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4565 } | |
4566 | |
4567 @override | |
4568 NumState times(InstanceState rightOperand) { | |
4569 assertNumOrNull(rightOperand); | |
4570 if (value == null) { | |
4571 if (rightOperand is DoubleState) { | |
4572 return DoubleState.UNKNOWN_VALUE; | |
4573 } | |
4574 return UNKNOWN_VALUE; | |
4575 } | |
4576 if (rightOperand is IntState) { | |
4577 int rightValue = rightOperand.value; | |
4578 if (rightValue == null) { | |
4579 return UNKNOWN_VALUE; | |
4580 } | |
4581 return new IntState(value * rightValue); | |
4582 } else if (rightOperand is DoubleState) { | |
4583 double rightValue = rightOperand.value; | |
4584 if (rightValue == null) { | |
4585 return DoubleState.UNKNOWN_VALUE; | |
4586 } | |
4587 return new DoubleState(value.toDouble() * rightValue); | |
4588 } else if (rightOperand is DynamicState || rightOperand is NumState) { | |
4589 return UNKNOWN_VALUE; | |
4590 } | |
4591 throw new EvaluationException( | |
4592 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4593 } | |
4594 | |
4595 @override | |
4596 String toString() => value == null ? "-unknown-" : value.toString(); | |
4597 } | |
4598 | |
4599 /** | |
4600 * The state of an object representing a list. | |
4601 */ | |
4602 class ListState extends InstanceState { | |
4603 /** | |
4604 * The elements of the list. | |
4605 */ | |
4606 final List<DartObjectImpl> _elements; | |
4607 | |
4608 /** | |
4609 * Initialize a newly created state to represent a list with the given | |
4610 * [elements]. | |
4611 */ | |
4612 ListState(this._elements); | |
4613 | |
4614 @override | |
4615 bool get hasExactValue { | |
4616 int count = _elements.length; | |
4617 for (int i = 0; i < count; i++) { | |
4618 if (!_elements[i].hasExactValue) { | |
4619 return false; | |
4620 } | |
4621 } | |
4622 return true; | |
4623 } | |
4624 | |
4625 @override | |
4626 int get hashCode { | |
4627 int value = 0; | |
4628 int count = _elements.length; | |
4629 for (int i = 0; i < count; i++) { | |
4630 value = (value << 3) ^ _elements[i].hashCode; | |
4631 } | |
4632 return value; | |
4633 } | |
4634 | |
4635 @override | |
4636 String get typeName => "List"; | |
4637 | |
4638 @override | |
4639 List<Object> get value { | |
4640 int count = _elements.length; | |
4641 List<Object> result = new List<Object>(count); | |
4642 for (int i = 0; i < count; i++) { | |
4643 DartObjectImpl element = _elements[i]; | |
4644 if (!element.hasExactValue) { | |
4645 return null; | |
4646 } | |
4647 result[i] = element.value; | |
4648 } | |
4649 return result; | |
4650 } | |
4651 | |
4652 @override | |
4653 bool operator ==(Object object) { | |
4654 if (object is! ListState) { | |
4655 return false; | |
4656 } | |
4657 List<DartObjectImpl> otherElements = (object as ListState)._elements; | |
4658 int count = _elements.length; | |
4659 if (otherElements.length != count) { | |
4660 return false; | |
4661 } else if (count == 0) { | |
4662 return true; | |
4663 } | |
4664 for (int i = 0; i < count; i++) { | |
4665 if (_elements[i] != otherElements[i]) { | |
4666 return false; | |
4667 } | |
4668 } | |
4669 return true; | |
4670 } | |
4671 | |
4672 @override | |
4673 StringState convertToString() => StringState.UNKNOWN_VALUE; | |
4674 | |
4675 @override | |
4676 BoolState equalEqual(InstanceState rightOperand) { | |
4677 assertBoolNumStringOrNull(rightOperand); | |
4678 return isIdentical(rightOperand); | |
4679 } | |
4680 | |
4681 @override | |
4682 BoolState isIdentical(InstanceState rightOperand) { | |
4683 if (rightOperand is DynamicState) { | |
4684 return BoolState.UNKNOWN_VALUE; | |
4685 } | |
4686 return BoolState.from(this == rightOperand); | |
4687 } | |
4688 | |
4689 @override | |
4690 String toString() { | |
4691 StringBuffer buffer = new StringBuffer(); | |
4692 buffer.write('['); | |
4693 bool first = true; | |
4694 _elements.forEach((DartObjectImpl element) { | |
4695 if (first) { | |
4696 first = false; | |
4697 } else { | |
4698 buffer.write(', '); | |
4699 } | |
4700 buffer.write(element); | |
4701 }); | |
4702 buffer.write(']'); | |
4703 return buffer.toString(); | |
4704 } | |
4705 } | |
4706 | |
4707 /** | |
4708 * The state of an object representing a map. | |
4709 */ | |
4710 class MapState extends InstanceState { | |
4711 /** | |
4712 * The entries in the map. | |
4713 */ | |
4714 final HashMap<DartObjectImpl, DartObjectImpl> _entries; | |
4715 | |
4716 /** | |
4717 * Initialize a newly created state to represent a map with the given | |
4718 * [entries]. | |
4719 */ | |
4720 MapState(this._entries); | |
4721 | |
4722 @override | |
4723 bool get hasExactValue { | |
4724 for (DartObjectImpl key in _entries.keys) { | |
4725 if (!key.hasExactValue || !_entries[key].hasExactValue) { | |
4726 return false; | |
4727 } | |
4728 } | |
4729 return true; | |
4730 } | |
4731 | |
4732 @override | |
4733 int get hashCode { | |
4734 int value = 0; | |
4735 for (DartObjectImpl key in _entries.keys.toSet()) { | |
4736 value = (value << 3) ^ key.hashCode; | |
4737 } | |
4738 return value; | |
4739 } | |
4740 | |
4741 @override | |
4742 String get typeName => "Map"; | |
4743 | |
4744 @override | |
4745 Map<Object, Object> get value { | |
4746 HashMap<Object, Object> result = new HashMap<Object, Object>(); | |
4747 for (DartObjectImpl key in _entries.keys) { | |
4748 DartObjectImpl value = _entries[key]; | |
4749 if (!key.hasExactValue || !value.hasExactValue) { | |
4750 return null; | |
4751 } | |
4752 result[key.value] = value.value; | |
4753 } | |
4754 return result; | |
4755 } | |
4756 | |
4757 @override | |
4758 bool operator ==(Object object) { | |
4759 if (object is! MapState) { | |
4760 return false; | |
4761 } | |
4762 HashMap<DartObjectImpl, DartObjectImpl> otherElements = | |
4763 (object as MapState)._entries; | |
4764 int count = _entries.length; | |
4765 if (otherElements.length != count) { | |
4766 return false; | |
4767 } else if (count == 0) { | |
4768 return true; | |
4769 } | |
4770 for (DartObjectImpl key in _entries.keys) { | |
4771 DartObjectImpl value = _entries[key]; | |
4772 DartObjectImpl otherValue = otherElements[key]; | |
4773 if (value != otherValue) { | |
4774 return false; | |
4775 } | |
4776 } | |
4777 return true; | |
4778 } | |
4779 | |
4780 @override | |
4781 StringState convertToString() => StringState.UNKNOWN_VALUE; | |
4782 | |
4783 @override | |
4784 BoolState equalEqual(InstanceState rightOperand) { | |
4785 assertBoolNumStringOrNull(rightOperand); | |
4786 return isIdentical(rightOperand); | |
4787 } | |
4788 | |
4789 @override | |
4790 BoolState isIdentical(InstanceState rightOperand) { | |
4791 if (rightOperand is DynamicState) { | |
4792 return BoolState.UNKNOWN_VALUE; | |
4793 } | |
4794 return BoolState.from(this == rightOperand); | |
4795 } | |
4796 | |
4797 @override | |
4798 String toString() { | |
4799 StringBuffer buffer = new StringBuffer(); | |
4800 buffer.write('{'); | |
4801 bool first = true; | |
4802 _entries.forEach((DartObjectImpl key, DartObjectImpl value) { | |
4803 if (first) { | |
4804 first = false; | |
4805 } else { | |
4806 buffer.write(', '); | |
4807 } | |
4808 buffer.write(key); | |
4809 buffer.write(' = '); | |
4810 buffer.write(value); | |
4811 }); | |
4812 buffer.write('}'); | |
4813 return buffer.toString(); | |
4814 } | |
4815 } | |
4816 | |
4817 /** | |
4818 * The state of an object representing the value 'null'. | |
4819 */ | |
4820 class NullState extends InstanceState { | |
4821 /** | |
4822 * An instance representing the boolean value 'null'. | |
4823 */ | |
4824 static NullState NULL_STATE = new NullState(); | |
4825 | |
4826 @override | |
4827 bool get hasExactValue => true; | |
4828 | |
4829 @override | |
4830 int get hashCode => 0; | |
4831 | |
4832 @override | |
4833 bool get isBoolNumStringOrNull => true; | |
4834 | |
4835 @override | |
4836 String get typeName => "Null"; | |
4837 | |
4838 @override | |
4839 bool operator ==(Object object) => object is NullState; | |
4840 | |
4841 @override | |
4842 BoolState convertToBool() { | |
4843 throw new EvaluationException( | |
4844 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4845 } | |
4846 | |
4847 @override | |
4848 StringState convertToString() => new StringState("null"); | |
4849 | |
4850 @override | |
4851 BoolState equalEqual(InstanceState rightOperand) { | |
4852 assertBoolNumStringOrNull(rightOperand); | |
4853 return isIdentical(rightOperand); | |
4854 } | |
4855 | |
4856 @override | |
4857 BoolState isIdentical(InstanceState rightOperand) { | |
4858 if (rightOperand is DynamicState) { | |
4859 return BoolState.UNKNOWN_VALUE; | |
4860 } | |
4861 return BoolState.from(rightOperand is NullState); | |
4862 } | |
4863 | |
4864 @override | |
4865 BoolState logicalNot() { | |
4866 throw new EvaluationException( | |
4867 CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION); | |
4868 } | |
4869 | |
4870 @override | |
4871 String toString() => "null"; | |
4872 } | |
4873 | |
4874 /** | |
4875 * The state of an object representing a number of an unknown type (a 'num'). | |
4876 */ | |
4877 class NumState extends InstanceState { | |
4878 /** | |
4879 * A state that can be used to represent a number whose value is not known. | |
4880 */ | |
4881 static NumState UNKNOWN_VALUE = new NumState(); | |
4882 | |
4883 @override | |
4884 int get hashCode => 7; | |
4885 | |
4886 @override | |
4887 bool get isBoolNumStringOrNull => true; | |
4888 | |
4889 @override | |
4890 bool get isUnknown => identical(this, UNKNOWN_VALUE); | |
4891 | |
4892 @override | |
4893 String get typeName => "num"; | |
4894 | |
4895 @override | |
4896 bool operator ==(Object object) => object is NumState; | |
4897 | |
4898 @override | |
4899 NumState add(InstanceState rightOperand) { | |
4900 assertNumOrNull(rightOperand); | |
4901 return UNKNOWN_VALUE; | |
4902 } | |
4903 | |
4904 @override | |
4905 StringState convertToString() => StringState.UNKNOWN_VALUE; | |
4906 | |
4907 @override | |
4908 NumState divide(InstanceState rightOperand) { | |
4909 assertNumOrNull(rightOperand); | |
4910 return DoubleState.UNKNOWN_VALUE; | |
4911 } | |
4912 | |
4913 @override | |
4914 BoolState equalEqual(InstanceState rightOperand) { | |
4915 assertBoolNumStringOrNull(rightOperand); | |
4916 return BoolState.UNKNOWN_VALUE; | |
4917 } | |
4918 | |
4919 @override | |
4920 BoolState greaterThan(InstanceState rightOperand) { | |
4921 assertNumOrNull(rightOperand); | |
4922 return BoolState.UNKNOWN_VALUE; | |
4923 } | |
4924 | |
4925 @override | |
4926 BoolState greaterThanOrEqual(InstanceState rightOperand) { | |
4927 assertNumOrNull(rightOperand); | |
4928 return BoolState.UNKNOWN_VALUE; | |
4929 } | |
4930 | |
4931 @override | |
4932 IntState integerDivide(InstanceState rightOperand) { | |
4933 assertNumOrNull(rightOperand); | |
4934 if (rightOperand is IntState) { | |
4935 int rightValue = rightOperand.value; | |
4936 if (rightValue == null) { | |
4937 return IntState.UNKNOWN_VALUE; | |
4938 } else if (rightValue == 0) { | |
4939 throw new EvaluationException( | |
4940 CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE); | |
4941 } | |
4942 } else if (rightOperand is DynamicState) { | |
4943 return IntState.UNKNOWN_VALUE; | |
4944 } | |
4945 return IntState.UNKNOWN_VALUE; | |
4946 } | |
4947 | |
4948 @override | |
4949 BoolState isIdentical(InstanceState rightOperand) { | |
4950 return BoolState.UNKNOWN_VALUE; | |
4951 } | |
4952 | |
4953 @override | |
4954 BoolState lessThan(InstanceState rightOperand) { | |
4955 assertNumOrNull(rightOperand); | |
4956 return BoolState.UNKNOWN_VALUE; | |
4957 } | |
4958 | |
4959 @override | |
4960 BoolState lessThanOrEqual(InstanceState rightOperand) { | |
4961 assertNumOrNull(rightOperand); | |
4962 return BoolState.UNKNOWN_VALUE; | |
4963 } | |
4964 | |
4965 @override | |
4966 NumState minus(InstanceState rightOperand) { | |
4967 assertNumOrNull(rightOperand); | |
4968 return UNKNOWN_VALUE; | |
4969 } | |
4970 | |
4971 @override | |
4972 NumState negated() => UNKNOWN_VALUE; | |
4973 | |
4974 @override | |
4975 NumState remainder(InstanceState rightOperand) { | |
4976 assertNumOrNull(rightOperand); | |
4977 return UNKNOWN_VALUE; | |
4978 } | |
4979 | |
4980 @override | |
4981 NumState times(InstanceState rightOperand) { | |
4982 assertNumOrNull(rightOperand); | |
4983 return UNKNOWN_VALUE; | |
4984 } | |
4985 | |
4986 @override | |
4987 String toString() => "-unknown-"; | |
4988 } | |
4989 | |
4990 /** | |
4991 * An object used to add reference information for a given variable to the | |
4992 * bi-directional mapping used to order the evaluation of constants. | |
4993 */ | |
4994 class ReferenceFinder extends RecursiveAstVisitor<Object> { | |
4995 /** | |
4996 * The callback which should be used to report any dependencies that were | |
4997 * found. | |
4998 */ | |
4999 final ReferenceFinderCallback _callback; | |
5000 | |
5001 /** | |
5002 * Initialize a newly created reference finder to find references from a given | |
5003 * variable to other variables and to add those references to the given graph. | |
5004 * The [_callback] will be invoked for every dependency found. | |
5005 */ | |
5006 ReferenceFinder(this._callback); | |
5007 | |
5008 @override | |
5009 Object visitInstanceCreationExpression(InstanceCreationExpression node) { | |
5010 if (node.isConst) { | |
5011 ConstructorElement constructor = | |
5012 ConstantEvaluationEngine._getConstructorBase(node.staticElement); | |
5013 if (constructor != null) { | |
5014 _callback(constructor); | |
5015 } | |
5016 } | |
5017 return super.visitInstanceCreationExpression(node); | |
5018 } | |
5019 | |
5020 @override | |
5021 Object visitLabel(Label node) { | |
5022 // We are visiting the "label" part of a named expression in a function | |
5023 // call (presumably a constructor call), e.g. "const C(label: ...)". We | |
5024 // don't want to visit the SimpleIdentifier for the label because that's a | |
5025 // reference to a function parameter that needs to be filled in; it's not a | |
5026 // constant whose value we depend on. | |
5027 return null; | |
5028 } | |
5029 | |
5030 @override | |
5031 Object visitRedirectingConstructorInvocation( | |
5032 RedirectingConstructorInvocation node) { | |
5033 super.visitRedirectingConstructorInvocation(node); | |
5034 ConstructorElement target = | |
5035 ConstantEvaluationEngine._getConstructorBase(node.staticElement); | |
5036 if (target != null) { | |
5037 _callback(target); | |
5038 } | |
5039 return null; | |
5040 } | |
5041 | |
5042 @override | |
5043 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
5044 Element element = node.staticElement; | |
5045 if (element is PropertyAccessorElement) { | |
5046 element = (element as PropertyAccessorElement).variable; | |
5047 } | |
5048 if (element is VariableElement) { | |
5049 _callback(element); | |
5050 } | |
5051 return null; | |
5052 } | |
5053 | |
5054 @override | |
5055 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
5056 super.visitSuperConstructorInvocation(node); | |
5057 ConstructorElement constructor = | |
5058 ConstantEvaluationEngine._getConstructorBase(node.staticElement); | |
5059 if (constructor != null) { | |
5060 _callback(constructor); | |
5061 } | |
5062 return null; | |
5063 } | |
5064 } | |
5065 | |
5066 /** | |
5067 * The state of an object representing a string. | |
5068 */ | |
5069 class StringState extends InstanceState { | |
5070 /** | |
5071 * A state that can be used to represent a double whose value is not known. | |
5072 */ | |
5073 static StringState UNKNOWN_VALUE = new StringState(null); | |
5074 | |
5075 /** | |
5076 * The value of this instance. | |
5077 */ | |
5078 final String value; | |
5079 | |
5080 /** | |
5081 * Initialize a newly created state to represent the given [value]. | |
5082 */ | |
5083 StringState(this.value); | |
5084 | |
5085 @override | |
5086 bool get hasExactValue => true; | |
5087 | |
5088 @override | |
5089 int get hashCode => value == null ? 0 : value.hashCode; | |
5090 | |
5091 @override | |
5092 bool get isBoolNumStringOrNull => true; | |
5093 | |
5094 @override | |
5095 bool get isUnknown => value == null; | |
5096 | |
5097 @override | |
5098 String get typeName => "String"; | |
5099 | |
5100 @override | |
5101 bool operator ==(Object object) => | |
5102 object is StringState && (value == object.value); | |
5103 | |
5104 @override | |
5105 StringState concatenate(InstanceState rightOperand) { | |
5106 if (value == null) { | |
5107 return UNKNOWN_VALUE; | |
5108 } | |
5109 if (rightOperand is StringState) { | |
5110 String rightValue = rightOperand.value; | |
5111 if (rightValue == null) { | |
5112 return UNKNOWN_VALUE; | |
5113 } | |
5114 return new StringState("$value$rightValue"); | |
5115 } else if (rightOperand is DynamicState) { | |
5116 return UNKNOWN_VALUE; | |
5117 } | |
5118 return super.concatenate(rightOperand); | |
5119 } | |
5120 | |
5121 @override | |
5122 StringState convertToString() => this; | |
5123 | |
5124 @override | |
5125 BoolState equalEqual(InstanceState rightOperand) { | |
5126 assertBoolNumStringOrNull(rightOperand); | |
5127 return isIdentical(rightOperand); | |
5128 } | |
5129 | |
5130 @override | |
5131 BoolState isIdentical(InstanceState rightOperand) { | |
5132 if (value == null) { | |
5133 return BoolState.UNKNOWN_VALUE; | |
5134 } | |
5135 if (rightOperand is StringState) { | |
5136 String rightValue = rightOperand.value; | |
5137 if (rightValue == null) { | |
5138 return BoolState.UNKNOWN_VALUE; | |
5139 } | |
5140 return BoolState.from(value == rightValue); | |
5141 } else if (rightOperand is DynamicState) { | |
5142 return BoolState.UNKNOWN_VALUE; | |
5143 } | |
5144 return BoolState.FALSE_STATE; | |
5145 } | |
5146 | |
5147 @override | |
5148 IntState stringLength() { | |
5149 if (value == null) { | |
5150 return IntState.UNKNOWN_VALUE; | |
5151 } | |
5152 return new IntState(value.length); | |
5153 } | |
5154 | |
5155 @override | |
5156 String toString() => value == null ? "-unknown-" : "'$value'"; | |
5157 } | |
5158 | |
5159 /** | |
5160 * The state of an object representing a symbol. | |
5161 */ | |
5162 class SymbolState extends InstanceState { | |
5163 /** | |
5164 * The value of this instance. | |
5165 */ | |
5166 final String value; | |
5167 | |
5168 /** | |
5169 * Initialize a newly created state to represent the given [value]. | |
5170 */ | |
5171 SymbolState(this.value); | |
5172 | |
5173 @override | |
5174 bool get hasExactValue => true; | |
5175 | |
5176 @override | |
5177 int get hashCode => value == null ? 0 : value.hashCode; | |
5178 | |
5179 @override | |
5180 String get typeName => "Symbol"; | |
5181 | |
5182 @override | |
5183 bool operator ==(Object object) => | |
5184 object is SymbolState && (value == object.value); | |
5185 | |
5186 @override | |
5187 StringState convertToString() { | |
5188 if (value == null) { | |
5189 return StringState.UNKNOWN_VALUE; | |
5190 } | |
5191 return new StringState(value); | |
5192 } | |
5193 | |
5194 @override | |
5195 BoolState equalEqual(InstanceState rightOperand) { | |
5196 assertBoolNumStringOrNull(rightOperand); | |
5197 return isIdentical(rightOperand); | |
5198 } | |
5199 | |
5200 @override | |
5201 BoolState isIdentical(InstanceState rightOperand) { | |
5202 if (value == null) { | |
5203 return BoolState.UNKNOWN_VALUE; | |
5204 } | |
5205 if (rightOperand is SymbolState) { | |
5206 String rightValue = rightOperand.value; | |
5207 if (rightValue == null) { | |
5208 return BoolState.UNKNOWN_VALUE; | |
5209 } | |
5210 return BoolState.from(value == rightValue); | |
5211 } else if (rightOperand is DynamicState) { | |
5212 return BoolState.UNKNOWN_VALUE; | |
5213 } | |
5214 return BoolState.FALSE_STATE; | |
5215 } | |
5216 | |
5217 @override | |
5218 String toString() => value == null ? "-unknown-" : "#$value"; | |
5219 } | |
5220 | |
5221 /** | |
5222 * The state of an object representing a type. | |
5223 */ | |
5224 class TypeState extends InstanceState { | |
5225 /** | |
5226 * The element representing the type being modeled. | |
5227 */ | |
5228 final Element _element; | |
5229 | |
5230 /** | |
5231 * Initialize a newly created state to represent the given [value]. | |
5232 */ | |
5233 TypeState(this._element); | |
5234 | |
5235 @override | |
5236 int get hashCode => _element == null ? 0 : _element.hashCode; | |
5237 | |
5238 @override | |
5239 String get typeName => "Type"; | |
5240 | |
5241 @override | |
5242 Element get value => _element; | |
5243 | |
5244 @override | |
5245 bool operator ==(Object object) => | |
5246 object is TypeState && (_element == object._element); | |
5247 | |
5248 @override | |
5249 StringState convertToString() { | |
5250 if (_element == null) { | |
5251 return StringState.UNKNOWN_VALUE; | |
5252 } | |
5253 return new StringState(_element.name); | |
5254 } | |
5255 | |
5256 @override | |
5257 BoolState equalEqual(InstanceState rightOperand) { | |
5258 assertBoolNumStringOrNull(rightOperand); | |
5259 return isIdentical(rightOperand); | |
5260 } | |
5261 | |
5262 @override | |
5263 BoolState isIdentical(InstanceState rightOperand) { | |
5264 if (_element == null) { | |
5265 return BoolState.UNKNOWN_VALUE; | |
5266 } | |
5267 if (rightOperand is TypeState) { | |
5268 Element rightElement = rightOperand._element; | |
5269 if (rightElement == null) { | |
5270 return BoolState.UNKNOWN_VALUE; | |
5271 } | |
5272 return BoolState.from(_element == rightElement); | |
5273 } else if (rightOperand is DynamicState) { | |
5274 return BoolState.UNKNOWN_VALUE; | |
5275 } | |
5276 return BoolState.FALSE_STATE; | |
5277 } | |
5278 | |
5279 @override | |
5280 String toString() => _element == null ? "-unknown-" : _element.name; | |
5281 } | |
OLD | NEW |