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