OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js.constants.evaluation; | 5 library dart2js.constants.evaluation; |
6 | 6 |
7 import '../compiler.dart' show | 7 import '../compiler.dart' show |
8 Compiler; | 8 Compiler; |
| 9 import '../core_types.dart'; |
| 10 import '../dart_types.dart'; |
| 11 import '../diagnostics/messages.dart' show |
| 12 MessageKind; |
| 13 import '../elements/elements.dart' show |
| 14 ConstructorElement, |
| 15 VariableElement; |
| 16 import '../resolution/operators.dart'; |
9 import '../universe/call_structure.dart' show | 17 import '../universe/call_structure.dart' show |
10 CallStructure; | 18 CallStructure; |
11 import 'expressions.dart'; | 19 import 'expressions.dart'; |
| 20 import 'values.dart'; |
12 | 21 |
13 /// Environment used for evaluating constant expressions. | 22 /// Environment used for evaluating constant expressions. |
14 abstract class Environment { | 23 abstract class Environment { |
15 // TODO(johnniwinther): Replace this with [CoreTypes] and maybe [Backend]. | 24 // TODO(johnniwinther): Replace this with [CoreTypes] and maybe [Backend]. |
16 Compiler get compiler; | 25 Compiler get compiler; |
17 | 26 |
| 27 /// The core types in the evaluation environment. |
| 28 CoreTypes get coreTypes; |
| 29 |
18 /// Read environments string passed in using the '-Dname=value' option. | 30 /// Read environments string passed in using the '-Dname=value' option. |
19 String readFromEnvironment(String name); | 31 String readFromEnvironment(String name); |
| 32 |
| 33 void reportWarning(ConstantExpression expression, |
| 34 MessageKind kind, |
| 35 Map arguments); |
| 36 |
| 37 void reportError(ConstantExpression expression, |
| 38 MessageKind kind, |
| 39 Map arguments); |
| 40 |
| 41 ConstantValue evaluateConstructor( |
| 42 ConstructorElement constructor, |
| 43 ConstantValue evaluate()); |
| 44 |
| 45 ConstantValue evaluateVariable( |
| 46 VariableElement variable, |
| 47 ConstantValue evaluate()); |
20 } | 48 } |
21 | 49 |
22 /// The normalized arguments passed to a const constructor computed from the | 50 /// The normalized arguments passed to a const constructor computed from the |
23 /// actual [arguments] and the [defaultValues] of the called construrctor. | 51 /// actual [arguments] and the [defaultValues] of the called construrctor. |
24 class NormalizedArguments { | 52 class NormalizedArguments { |
25 final Map<dynamic/*int|String*/, ConstantExpression> defaultValues; | 53 final Map<dynamic/*int|String*/, ConstantExpression> defaultValues; |
26 final CallStructure callStructure; | 54 final CallStructure callStructure; |
27 final List<ConstantExpression> arguments; | 55 final List<ConstantExpression> arguments; |
28 | 56 |
29 NormalizedArguments(this.defaultValues, this.callStructure, this.arguments); | 57 NormalizedArguments(this.defaultValues, this.callStructure, this.arguments); |
(...skipping 10 matching lines...) Expand all Loading... |
40 | 68 |
41 /// Returns the normalized [index]th positional argument. | 69 /// Returns the normalized [index]th positional argument. |
42 ConstantExpression getPositionalArgument(int index) { | 70 ConstantExpression getPositionalArgument(int index) { |
43 if (index >= callStructure.positionalArgumentCount) { | 71 if (index >= callStructure.positionalArgumentCount) { |
44 // The positional argument is not provided. | 72 // The positional argument is not provided. |
45 return defaultValues[index]; | 73 return defaultValues[index]; |
46 } | 74 } |
47 return arguments[index]; | 75 return arguments[index]; |
48 } | 76 } |
49 } | 77 } |
| 78 |
| 79 abstract class ConstantTypeChecker<T> { |
| 80 CoreTypes get coreTypes; |
| 81 bool get isRequired; |
| 82 bool get allowUnknown; |
| 83 |
| 84 void reportError(T position, MessageKind messageKind, Map arguments); |
| 85 |
| 86 bool isBool(DartType type, {bool allowUnknown}) { |
| 87 if (allowUnknown == null) allowUnknown = this.allowUnknown; |
| 88 return |
| 89 type == coreTypes.boolType || |
| 90 (type == null && allowUnknown); |
| 91 } |
| 92 |
| 93 bool isBoolOrNull(DartType type, {bool allowUnknown}) { |
| 94 if (allowUnknown == null) allowUnknown = this.allowUnknown; |
| 95 return |
| 96 type == coreTypes.boolType || |
| 97 type == coreTypes.nullType || |
| 98 (type == null && allowUnknown); |
| 99 } |
| 100 |
| 101 bool isInt(DartType type, {bool allowUnknown}) { |
| 102 if (allowUnknown == null) allowUnknown = this.allowUnknown; |
| 103 return |
| 104 type == coreTypes.intType || |
| 105 (type == null && allowUnknown); |
| 106 } |
| 107 |
| 108 bool isIntOrNull(DartType type, {bool allowUnknown}) { |
| 109 if (allowUnknown == null) allowUnknown = this.allowUnknown; |
| 110 return |
| 111 type == coreTypes.intType || |
| 112 type == coreTypes.nullType || |
| 113 (type == null && allowUnknown); |
| 114 } |
| 115 |
| 116 bool isNum(DartType type, {bool allowUnknown}) { |
| 117 if (allowUnknown == null) allowUnknown = this.allowUnknown; |
| 118 return |
| 119 type == coreTypes.intType || |
| 120 type == coreTypes.doubleType || |
| 121 (type == null && allowUnknown); |
| 122 } |
| 123 |
| 124 bool isString(DartType type, {bool allowUnknown}) { |
| 125 if (allowUnknown == null) allowUnknown = this.allowUnknown; |
| 126 return |
| 127 type == coreTypes.stringType || |
| 128 (type == null && allowUnknown); |
| 129 } |
| 130 |
| 131 bool isStringOrNull(DartType type, {bool allowUnknown}) { |
| 132 if (allowUnknown == null) allowUnknown = this.allowUnknown; |
| 133 return |
| 134 type == coreTypes.stringType || |
| 135 type == coreTypes.nullType || |
| 136 (type == null && allowUnknown); |
| 137 } |
| 138 |
| 139 bool isPrimitive(DartType type, {bool allowUnknown}) { |
| 140 if (allowUnknown == null) allowUnknown = this.allowUnknown; |
| 141 return |
| 142 type == coreTypes.intType || |
| 143 type == coreTypes.doubleType || |
| 144 type == coreTypes.stringType || |
| 145 type == coreTypes.boolType || |
| 146 type == coreTypes.nullType || |
| 147 (type == null && allowUnknown); |
| 148 } |
| 149 |
| 150 bool isNonNullPrimitive(DartType type, {bool allowUnknown}) { |
| 151 if (allowUnknown == null) allowUnknown = this.allowUnknown; |
| 152 return |
| 153 type == coreTypes.boolType || |
| 154 type == coreTypes.intType || |
| 155 type == coreTypes.doubleType || |
| 156 type == coreTypes.stringType || |
| 157 (type == null && allowUnknown); |
| 158 } |
| 159 |
| 160 bool checkBinaryExpression( |
| 161 T position, |
| 162 ConstantTypeInfo<T> left, |
| 163 BinaryOperator operator, |
| 164 ConstantTypeInfo<T> right) { |
| 165 bool isValid = true; |
| 166 switch (operator.kind) { |
| 167 case BinaryOperatorKind.EQ: |
| 168 case BinaryOperatorKind.NOT_EQ: |
| 169 if (!isPrimitive(left.type)) { |
| 170 if (isRequired) { |
| 171 reportError( |
| 172 left.position, |
| 173 MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE, |
| 174 {'constant': left.constant, |
| 175 'type': left.type, |
| 176 'operator': operator}); |
| 177 } |
| 178 isValid = false; |
| 179 } |
| 180 if (!isPrimitive(right.type)) { |
| 181 if (isRequired) { |
| 182 reportError( |
| 183 right.position, |
| 184 MessageKind.INVALID_CONSTANT_BINARY_PRIMITIVE_TYPE, |
| 185 {'constant': right.constant, |
| 186 'type': right.type, |
| 187 'operator': operator}); |
| 188 } |
| 189 isValid = false; |
| 190 } |
| 191 break; |
| 192 case BinaryOperatorKind.ADD: |
| 193 if (isString(left.type, allowUnknown: false)) { |
| 194 if (!isString(right.type)) { |
| 195 if (isRequired) { |
| 196 reportError( |
| 197 right.position, |
| 198 MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE, |
| 199 {'constant': right.constant, |
| 200 'type': right.type}); |
| 201 } |
| 202 isValid = false; |
| 203 } |
| 204 } else if (isNum(left.type, allowUnknown: false)) { |
| 205 if (!isNum(right.type)) { |
| 206 if (isRequired) { |
| 207 reportError( |
| 208 right.position, |
| 209 MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE, |
| 210 {'constant': right.constant, |
| 211 'type': right.type}); |
| 212 } |
| 213 isValid = false; |
| 214 } |
| 215 } else if (isString(right.type, allowUnknown: false)) { |
| 216 if (!isString(left.type)) { |
| 217 if (isRequired) { |
| 218 reportError( |
| 219 left.position, |
| 220 MessageKind.INVALID_CONSTANT_STRING_ADD_TYPE, |
| 221 {'constant': left.constant, |
| 222 'type': left.type}); |
| 223 } |
| 224 isValid = false; |
| 225 } |
| 226 } else if (isNum(right.type, allowUnknown: false)) { |
| 227 if (!isNum(left.type)) { |
| 228 if (isRequired) { |
| 229 reportError( |
| 230 left.position, |
| 231 MessageKind.INVALID_CONSTANT_NUM_ADD_TYPE, |
| 232 {'constant': left.constant, |
| 233 'type': left.type}); |
| 234 } |
| 235 isValid = false; |
| 236 } |
| 237 } else if (allowUnknown && (left.type == null || right.type == null)) { |
| 238 // Assume valid on unknown types. |
| 239 } else { |
| 240 if (isRequired) { |
| 241 assert(left.type != null); |
| 242 assert(right.type != null); |
| 243 reportError( |
| 244 position, |
| 245 MessageKind.INVALID_CONSTANT_ADD_TYPES, |
| 246 {'leftConstant': left.constant, |
| 247 'leftType': left.type, |
| 248 'rightConstant': right.constant, |
| 249 'rightType': right.type}); |
| 250 } |
| 251 isValid = false; |
| 252 } |
| 253 break; |
| 254 case BinaryOperatorKind.SUB: |
| 255 case BinaryOperatorKind.MUL: |
| 256 case BinaryOperatorKind.DIV: |
| 257 case BinaryOperatorKind.IDIV: |
| 258 case BinaryOperatorKind.MOD: |
| 259 case BinaryOperatorKind.GTEQ: |
| 260 case BinaryOperatorKind.GT: |
| 261 case BinaryOperatorKind.LTEQ: |
| 262 case BinaryOperatorKind.LT: |
| 263 if (!isNum(left.type)) { |
| 264 if (isRequired) { |
| 265 reportError( |
| 266 left.position, |
| 267 MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE, |
| 268 {'constant': left.constant, |
| 269 'type': left.type, |
| 270 'operator': operator}); |
| 271 } |
| 272 isValid = false; |
| 273 } |
| 274 if (!isNum(right.type)) { |
| 275 if (isRequired) { |
| 276 reportError( |
| 277 right.position, |
| 278 MessageKind.INVALID_CONSTANT_BINARY_NUM_TYPE, |
| 279 {'constant': right.constant, |
| 280 'type': right.type, |
| 281 'operator': operator}); |
| 282 } |
| 283 isValid = false; |
| 284 } |
| 285 break; |
| 286 case BinaryOperatorKind.SHL: |
| 287 case BinaryOperatorKind.SHR: |
| 288 case BinaryOperatorKind.AND: |
| 289 case BinaryOperatorKind.OR: |
| 290 case BinaryOperatorKind.XOR: |
| 291 if (!isInt(left.type)) { |
| 292 if (isRequired) { |
| 293 reportError( |
| 294 left.position, |
| 295 MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE, |
| 296 {'constant': left.constant, |
| 297 'type': left.type, |
| 298 'operator': operator}); |
| 299 } |
| 300 isValid = false; |
| 301 } |
| 302 if (!isInt(right.type)) { |
| 303 if (isRequired) { |
| 304 reportError( |
| 305 right.position, |
| 306 MessageKind.INVALID_CONSTANT_BINARY_INT_TYPE, |
| 307 {'constant': right.constant, |
| 308 'type': right.type, |
| 309 'operator': operator}); |
| 310 } |
| 311 isValid = false; |
| 312 } |
| 313 break; |
| 314 case BinaryOperatorKind.LOGICAL_AND: |
| 315 if (!isBool(left.type)) { |
| 316 if (isRequired) { |
| 317 reportError( |
| 318 left.position, |
| 319 MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE, |
| 320 {'constant': left.constant, |
| 321 'type': left.type, |
| 322 'operator': operator}); |
| 323 } |
| 324 isValid = false; |
| 325 } |
| 326 if (!isBool(right.type)) { |
| 327 if (isRequired) { |
| 328 reportError( |
| 329 right.position, |
| 330 MessageKind.INVALID_LOGICAL_AND_OPERAND_TYPE, |
| 331 {'constant': right.constant, |
| 332 'type': right.type, |
| 333 'operator': operator}); |
| 334 } |
| 335 isValid = false; |
| 336 } |
| 337 break; |
| 338 case BinaryOperatorKind.LOGICAL_OR: |
| 339 if (!isBool(left.type)) { |
| 340 if (isRequired) { |
| 341 reportError( |
| 342 left.position, |
| 343 MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE, |
| 344 {'constant': left.constant, |
| 345 'type': left.type, |
| 346 'operator': operator}); |
| 347 } |
| 348 isValid = false; |
| 349 } |
| 350 if (!isBool(right.type)) { |
| 351 if (isRequired) { |
| 352 reportError( |
| 353 right.position, |
| 354 MessageKind.INVALID_LOGICAL_OR_OPERAND_TYPE, |
| 355 {'constant': right.constant, |
| 356 'type': right.type, |
| 357 'operator': operator}); |
| 358 } |
| 359 isValid = false; |
| 360 } |
| 361 break; |
| 362 case BinaryOperatorKind.INDEX: |
| 363 if (isRequired) { |
| 364 reportError(position, MessageKind.INVALID_CONSTANT_INDEX, {}); |
| 365 } |
| 366 isValid = false; |
| 367 break; |
| 368 case BinaryOperatorKind.IF_NULL: |
| 369 if (isRequired) { |
| 370 reportError(position, MessageKind.INVALID_CONSTANT_IF_NULL, {}); |
| 371 } |
| 372 isValid = false; |
| 373 break; |
| 374 } |
| 375 return isValid; |
| 376 } |
| 377 |
| 378 bool checkFromEnvironment(ConstantTypeInfo<T> name) { |
| 379 bool isValidAsConstant = true; |
| 380 if (!isString(name.type)) { |
| 381 if (isRequired) { |
| 382 reportError( |
| 383 name.position, |
| 384 MessageKind.INVALID_FROM_ENVIRONMENT_NAME_TYPE, |
| 385 {'constant': name.constant, |
| 386 'type': name.type}); |
| 387 } |
| 388 isValidAsConstant = false; |
| 389 } |
| 390 return isValidAsConstant; |
| 391 } |
| 392 |
| 393 bool checkBoolFromEnvironment( |
| 394 ConstantTypeInfo<T> name, |
| 395 ConstantTypeInfo<T> defaultValue) { |
| 396 bool isValidAsConstant = checkFromEnvironment(name); |
| 397 if (defaultValue != null) { |
| 398 if (!isBoolOrNull(defaultValue.type)) { |
| 399 if (isRequired) { |
| 400 reportError( |
| 401 defaultValue.position, |
| 402 MessageKind.INVALID_BOOL_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE, |
| 403 {'constant': defaultValue.constant, |
| 404 'type': defaultValue.type}); |
| 405 } |
| 406 isValidAsConstant = false; |
| 407 } |
| 408 } |
| 409 return isValidAsConstant; |
| 410 } |
| 411 |
| 412 bool checkIntFromEnvironment( |
| 413 ConstantTypeInfo<T> name, |
| 414 ConstantTypeInfo<T> defaultValue) { |
| 415 bool isValidAsConstant = checkFromEnvironment(name); |
| 416 if (defaultValue != null) { |
| 417 if (!isIntOrNull(defaultValue.type)) { |
| 418 if (isRequired) { |
| 419 reportError( |
| 420 defaultValue.position, |
| 421 MessageKind.INVALID_INT_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE, |
| 422 {'constant': defaultValue.constant, |
| 423 'type': defaultValue.type}); |
| 424 } |
| 425 isValidAsConstant = false; |
| 426 } |
| 427 } |
| 428 return isValidAsConstant; |
| 429 } |
| 430 |
| 431 bool checkStringFromEnvironment( |
| 432 ConstantTypeInfo<T> name, |
| 433 ConstantTypeInfo<T> defaultValue) { |
| 434 bool isValidAsConstant = checkFromEnvironment(name); |
| 435 if (defaultValue != null) { |
| 436 if (!isStringOrNull(defaultValue.type)) { |
| 437 if (isRequired) { |
| 438 reportError( |
| 439 defaultValue.position, |
| 440 MessageKind.INVALID_STRING_FROM_ENVIRONMENT_DEFAULT_VALUE_TYPE, |
| 441 {'constant': defaultValue.constant, |
| 442 'type': defaultValue.type}); |
| 443 } |
| 444 isValidAsConstant = false; |
| 445 } |
| 446 } |
| 447 return isValidAsConstant; |
| 448 } |
| 449 |
| 450 bool checkConcatenate(ConstantTypeInfo<T> part) { |
| 451 bool isValidAsConstant = true; |
| 452 if (!isNonNullPrimitive(part.type)) { |
| 453 if (isRequired) { |
| 454 reportError( |
| 455 part.position, |
| 456 MessageKind.INVALID_CONSTANT_INTERPOLATION_TYPE, |
| 457 {'constant': part.constant, |
| 458 'type': part.type}); |
| 459 } |
| 460 isValidAsConstant = false; |
| 461 } |
| 462 return isValidAsConstant; |
| 463 } |
| 464 |
| 465 bool checkConditional(ConstantTypeInfo<T> condition) { |
| 466 bool isValidAsConstant = true; |
| 467 if (!isBool(condition.type)) { |
| 468 reportError( |
| 469 condition.position, |
| 470 MessageKind.INVALID_CONSTANT_CONDITIONAL_TYPE, |
| 471 {'constant': condition.constant, |
| 472 'type': condition.type}); |
| 473 isValidAsConstant = false; |
| 474 } |
| 475 return isValidAsConstant; |
| 476 } |
| 477 |
| 478 |
| 479 bool checkUnary( |
| 480 UnaryOperator operator, |
| 481 ConstantTypeInfo<T> expression) { |
| 482 bool isValid = true; |
| 483 switch (operator.kind) { |
| 484 case UnaryOperatorKind.NOT: |
| 485 if (!isBool(expression.type)) { |
| 486 if (isRequired) { |
| 487 reportError( |
| 488 expression.position, |
| 489 MessageKind.INVALID_CONSTANT_NOT_TYPE, |
| 490 {'constant': expression.constant, |
| 491 'type': expression.type, |
| 492 'operator': operator}); |
| 493 } |
| 494 isValid = false; |
| 495 } |
| 496 break; |
| 497 case UnaryOperatorKind.NEGATE: |
| 498 if (!isNum(expression.type)) { |
| 499 if (isRequired) { |
| 500 reportError( |
| 501 expression.position, |
| 502 MessageKind.INVALID_CONSTANT_NEGATE_TYPE, |
| 503 {'constant': expression.constant, |
| 504 'type': expression.type, |
| 505 'operator': operator}); |
| 506 } |
| 507 isValid = false; |
| 508 } |
| 509 break; |
| 510 case UnaryOperatorKind.COMPLEMENT: |
| 511 if (!isInt(expression.type)) { |
| 512 if (isRequired) { |
| 513 reportError( |
| 514 expression.position, |
| 515 MessageKind.INVALID_CONSTANT_COMPLEMENT_TYPE, |
| 516 {'constant': expression.constant, |
| 517 'type': expression.type, |
| 518 'operator': operator}); |
| 519 } |
| 520 isValid = false; |
| 521 } |
| 522 break; |
| 523 } |
| 524 return isValid; |
| 525 } |
| 526 } |
| 527 |
| 528 abstract class ConstantTypeInfo<T> { |
| 529 T get position; |
| 530 ConstantExpression get constant; |
| 531 DartType get type; |
| 532 } |
| 533 |
| 534 class ConstantExpressionChecker |
| 535 extends ConstantTypeChecker<ConstantExpression> { |
| 536 final Environment environment; |
| 537 |
| 538 ConstantExpressionChecker(this.environment); |
| 539 |
| 540 @override |
| 541 bool get allowUnknown => false; |
| 542 |
| 543 @override |
| 544 CoreTypes get coreTypes => environment.coreTypes; |
| 545 |
| 546 @override |
| 547 bool get isRequired => true; |
| 548 |
| 549 @override |
| 550 void reportError(ConstantExpression position, |
| 551 MessageKind messageKind, |
| 552 Map arguments) { |
| 553 environment.reportError(position, messageKind, arguments); |
| 554 } |
| 555 |
| 556 ConstantValueTypeInfo createInfo(ConstantExpression expression, |
| 557 ConstantValue value) { |
| 558 if (expression == null) return null; |
| 559 return new ConstantValueTypeInfo(expression, value.getType(coreTypes)); |
| 560 } |
| 561 } |
| 562 |
| 563 class ConstantValueTypeInfo implements ConstantTypeInfo<ConstantExpression> { |
| 564 final ConstantExpression constant; |
| 565 final DartType type; |
| 566 |
| 567 ConstantValueTypeInfo(this.constant, this.type); |
| 568 |
| 569 ConstantExpression get position => constant; |
| 570 } |
OLD | NEW |