| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, 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 class Constant implements Hashable { | |
| 6 const Constant(); | |
| 7 | |
| 8 bool isNull() => false; | |
| 9 bool isBool() => false; | |
| 10 bool isTrue() => false; | |
| 11 bool isFalse() => false; | |
| 12 bool isInt() => false; | |
| 13 bool isDouble() => false; | |
| 14 bool isNum() => false; | |
| 15 bool isString() => false; | |
| 16 bool isList() => false; | |
| 17 bool isMap() => false; | |
| 18 bool isConstructedObject() => false; | |
| 19 /** Returns true if the constant is null, a bool, a number or a string. */ | |
| 20 bool isPrimitive() => false; | |
| 21 /** Returns true if the constant is a list, a map or a constructed object. */ | |
| 22 bool isObject() => false; | |
| 23 | |
| 24 abstract void writeJsCode(StringBuffer buffer, ConstantHandler handler); | |
| 25 /** | |
| 26 * Unless the constant can be emitted multiple times (as for numbers and | |
| 27 * strings) adds its canonical name to the buffer. | |
| 28 */ | |
| 29 abstract void writeCanonicalizedJsCode(StringBuffer buffer, | |
| 30 ConstantHandler handler); | |
| 31 abstract List<Constant> getDependencies(); | |
| 32 } | |
| 33 | |
| 34 class PrimitiveConstant extends Constant { | |
| 35 abstract get value(); | |
| 36 const PrimitiveConstant(); | |
| 37 bool isPrimitive() => true; | |
| 38 | |
| 39 bool operator ==(var other) { | |
| 40 if (other is !PrimitiveConstant) return false; | |
| 41 PrimitiveConstant otherPrimitive = other; | |
| 42 // We use == instead of === so that DartStrings compare correctly. | |
| 43 return value == otherPrimitive.value; | |
| 44 } | |
| 45 | |
| 46 String toString() => value.toString(); | |
| 47 // Primitive constants don't have dependencies. | |
| 48 List<Constant> getDependencies() => const <Constant>[]; | |
| 49 abstract DartString toDartString(); | |
| 50 | |
| 51 void writeCanonicalizedJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 52 writeJsCode(buffer, handler); | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 class NullConstant extends PrimitiveConstant { | |
| 57 factory NullConstant() => const NullConstant._internal(); | |
| 58 const NullConstant._internal(); | |
| 59 bool isNull() => true; | |
| 60 get value() => null; | |
| 61 | |
| 62 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 63 buffer.add("(void 0)"); | |
| 64 } | |
| 65 | |
| 66 // The magic constant has no meaning. It is just a random value. | |
| 67 int hashCode() => 785965825; | |
| 68 DartString toDartString() => const LiteralDartString("null"); | |
| 69 } | |
| 70 | |
| 71 class NumConstant extends PrimitiveConstant { | |
| 72 abstract num get value(); | |
| 73 const NumConstant(); | |
| 74 bool isNum() => true; | |
| 75 } | |
| 76 | |
| 77 class IntConstant extends NumConstant { | |
| 78 final int value; | |
| 79 factory IntConstant(int value) { | |
| 80 switch(value) { | |
| 81 case 0: return const IntConstant._internal(0); | |
| 82 case 1: return const IntConstant._internal(1); | |
| 83 case 2: return const IntConstant._internal(2); | |
| 84 case 3: return const IntConstant._internal(3); | |
| 85 case 4: return const IntConstant._internal(4); | |
| 86 case 5: return const IntConstant._internal(5); | |
| 87 case 6: return const IntConstant._internal(6); | |
| 88 case 7: return const IntConstant._internal(7); | |
| 89 case 8: return const IntConstant._internal(8); | |
| 90 case 9: return const IntConstant._internal(9); | |
| 91 case 10: return const IntConstant._internal(10); | |
| 92 case -1: return const IntConstant._internal(-1); | |
| 93 case -2: return const IntConstant._internal(-2); | |
| 94 default: return new IntConstant._internal(value); | |
| 95 } | |
| 96 } | |
| 97 const IntConstant._internal(this.value); | |
| 98 bool isInt() => true; | |
| 99 | |
| 100 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 101 buffer.add("$value"); | |
| 102 } | |
| 103 | |
| 104 // We have to override the equality operator so that ints and doubles are | |
| 105 // treated as separate constants. | |
| 106 // The is [:!IntConstant:] check at the beginning of the function makes sure | |
| 107 // that we compare only equal to integer constants. | |
| 108 bool operator ==(var other) { | |
| 109 if (other is !IntConstant) return false; | |
| 110 IntConstant otherInt = other; | |
| 111 return value == otherInt.value; | |
| 112 } | |
| 113 | |
| 114 int hashCode() => value.hashCode(); | |
| 115 DartString toDartString() => new DartString.literal(value.toString()); | |
| 116 } | |
| 117 | |
| 118 class DoubleConstant extends NumConstant { | |
| 119 final double value; | |
| 120 factory DoubleConstant(double value) { | |
| 121 if (value.isNaN()) { | |
| 122 return const DoubleConstant._internal(double.NAN); | |
| 123 } else if (value == double.INFINITY) { | |
| 124 return const DoubleConstant._internal(double.INFINITY); | |
| 125 } else if (value == -double.INFINITY) { | |
| 126 return const DoubleConstant._internal(-double.INFINITY); | |
| 127 } else if (value == 0.0 && !value.isNegative()) { | |
| 128 return const DoubleConstant._internal(0.0); | |
| 129 } else if (value == 1.0) { | |
| 130 return const DoubleConstant._internal(1.0); | |
| 131 } else { | |
| 132 return new DoubleConstant._internal(value); | |
| 133 } | |
| 134 } | |
| 135 const DoubleConstant._internal(this.value); | |
| 136 bool isDouble() => true; | |
| 137 | |
| 138 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 139 if (value.isNaN()) { | |
| 140 buffer.add("(0/0)"); | |
| 141 } else if (value == double.INFINITY) { | |
| 142 buffer.add("(1/0)"); | |
| 143 } else if (value == -double.INFINITY) { | |
| 144 buffer.add("(-1/0)"); | |
| 145 } else { | |
| 146 buffer.add("$value"); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 bool operator ==(var other) { | |
| 151 if (other is !DoubleConstant) return false; | |
| 152 DoubleConstant otherDouble = other; | |
| 153 double otherValue = otherDouble.value; | |
| 154 if (value == 0.0 && otherValue == 0.0) { | |
| 155 return value.isNegative() == otherValue.isNegative(); | |
| 156 } else if (value.isNaN()) { | |
| 157 return otherValue.isNaN(); | |
| 158 } else { | |
| 159 return value == otherValue; | |
| 160 } | |
| 161 } | |
| 162 | |
| 163 int hashCode() => value.hashCode(); | |
| 164 DartString toDartString() => new DartString.literal(value.toString()); | |
| 165 } | |
| 166 | |
| 167 class BoolConstant extends PrimitiveConstant { | |
| 168 factory BoolConstant(value) { | |
| 169 return value ? new TrueConstant() : new FalseConstant(); | |
| 170 } | |
| 171 const BoolConstant._internal(); | |
| 172 bool isBool() => true; | |
| 173 | |
| 174 BoolConstant unaryFold(String op) { | |
| 175 if (op == "!") return new BoolConstant(!value); | |
| 176 return null; | |
| 177 } | |
| 178 | |
| 179 abstract BoolConstant negate(); | |
| 180 } | |
| 181 | |
| 182 class TrueConstant extends BoolConstant { | |
| 183 final bool value = true; | |
| 184 | |
| 185 factory TrueConstant() => const TrueConstant._internal(); | |
| 186 const TrueConstant._internal() : super._internal(); | |
| 187 bool isTrue() => true; | |
| 188 | |
| 189 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 190 buffer.add("true"); | |
| 191 } | |
| 192 | |
| 193 FalseConstant negate() => new FalseConstant(); | |
| 194 | |
| 195 bool operator ==(var other) => this === other; | |
| 196 // The magic constant is just a random value. It does not have any | |
| 197 // significance. | |
| 198 int hashCode() => 499; | |
| 199 DartString toDartString() => const LiteralDartString("true"); | |
| 200 } | |
| 201 | |
| 202 class FalseConstant extends BoolConstant { | |
| 203 final bool value = false; | |
| 204 | |
| 205 factory FalseConstant() => const FalseConstant._internal(); | |
| 206 const FalseConstant._internal() : super._internal(); | |
| 207 bool isFalse() => true; | |
| 208 | |
| 209 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 210 buffer.add("false"); | |
| 211 } | |
| 212 | |
| 213 TrueConstant negate() => new TrueConstant(); | |
| 214 | |
| 215 bool operator ==(var other) => this === other; | |
| 216 // The magic constant is just a random value. It does not have any | |
| 217 // significance. | |
| 218 int hashCode() => 536555975; | |
| 219 DartString toDartString() => const LiteralDartString("false"); | |
| 220 } | |
| 221 | |
| 222 class StringConstant extends PrimitiveConstant { | |
| 223 final DartString value; | |
| 224 int _hashCode; | |
| 225 | |
| 226 StringConstant(this.value) { | |
| 227 // TODO(floitsch): cache StringConstants. | |
| 228 // TODO(floitsch): compute hashcode without calling toString() on the | |
| 229 // DartString. | |
| 230 _hashCode = value.slowToString().hashCode(); | |
| 231 } | |
| 232 bool isString() => true; | |
| 233 | |
| 234 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 235 buffer.add("'"); | |
| 236 ConstantHandler.writeEscapedString(value, buffer, (reason) { | |
| 237 throw new CompilerCancelledException(reason); | |
| 238 }); | |
| 239 buffer.add("'"); | |
| 240 } | |
| 241 | |
| 242 bool operator ==(var other) { | |
| 243 if (other is !StringConstant) return false; | |
| 244 StringConstant otherString = other; | |
| 245 return (_hashCode == otherString._hashCode) && (value == otherString.value); | |
| 246 } | |
| 247 | |
| 248 int hashCode() => _hashCode; | |
| 249 DartString toDartString() => value; | |
| 250 } | |
| 251 | |
| 252 class ObjectConstant extends Constant { | |
| 253 final Type type; | |
| 254 | |
| 255 ObjectConstant(this.type); | |
| 256 bool isObject() => true; | |
| 257 | |
| 258 // TODO(1603): The class should be marked as abstract, but the VM doesn't | |
| 259 // currently allow this. | |
| 260 abstract int hashCode(); | |
| 261 | |
| 262 void writeCanonicalizedJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 263 String name = handler.getNameForConstant(this); | |
| 264 String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype"; | |
| 265 buffer.add("$isolatePrototype.$name"); | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 class ListConstant extends ObjectConstant { | |
| 270 final List<Constant> entries; | |
| 271 int _hashCode; | |
| 272 | |
| 273 ListConstant(Type type, this.entries) : super(type) { | |
| 274 // TODO(floitsch): create a better hash. | |
| 275 int hash = 0; | |
| 276 for (Constant input in entries) hash ^= input.hashCode(); | |
| 277 _hashCode = hash; | |
| 278 } | |
| 279 bool isList() => true; | |
| 280 | |
| 281 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 282 // TODO(floitsch): we should not need to go through the compiler to make | |
| 283 // the list constant. | |
| 284 String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype"; | |
| 285 buffer.add("$isolatePrototype.makeConstantList"); | |
| 286 buffer.add("(["); | |
| 287 for (int i = 0; i < entries.length; i++) { | |
| 288 if (i != 0) buffer.add(", "); | |
| 289 Constant entry = entries[i]; | |
| 290 entry.writeCanonicalizedJsCode(buffer, handler); | |
| 291 } | |
| 292 buffer.add("])"); | |
| 293 } | |
| 294 | |
| 295 bool operator ==(var other) { | |
| 296 if (other is !ListConstant) return false; | |
| 297 ListConstant otherList = other; | |
| 298 if (hashCode() != otherList.hashCode()) return false; | |
| 299 // TODO(floitsch): verify that the generic types are the same. | |
| 300 if (entries.length != otherList.entries.length) return false; | |
| 301 for (int i = 0; i < entries.length; i++) { | |
| 302 if (entries[i] != otherList.entries[i]) return false; | |
| 303 } | |
| 304 return true; | |
| 305 } | |
| 306 | |
| 307 int hashCode() => _hashCode; | |
| 308 | |
| 309 List<Constant> getDependencies() => entries; | |
| 310 } | |
| 311 | |
| 312 class MapConstant extends ObjectConstant { | |
| 313 /** The dart class implementing constant map literals. */ | |
| 314 static final SourceString DART_CLASS = const SourceString("ConstantMap"); | |
| 315 static final SourceString LENGTH_NAME = const SourceString("length"); | |
| 316 static final SourceString JS_OBJECT_NAME = const SourceString("_jsObject"); | |
| 317 static final SourceString KEYS_NAME = const SourceString("_keys"); | |
| 318 | |
| 319 final ListConstant keys; | |
| 320 final List<Constant> values; | |
| 321 int _hashCode; | |
| 322 | |
| 323 MapConstant(Type type, this.keys, this.values) : super(type) { | |
| 324 // TODO(floitsch): create a better hash. | |
| 325 int hash = 0; | |
| 326 for (Constant value in values) hash ^= value.hashCode(); | |
| 327 _hashCode = hash; | |
| 328 } | |
| 329 bool isMap() => true; | |
| 330 | |
| 331 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 332 | |
| 333 void writeJsMap() { | |
| 334 buffer.add("{"); | |
| 335 for (int i = 0; i < keys.entries.length; i++) { | |
| 336 if (i != 0) buffer.add(", "); | |
| 337 | |
| 338 StringConstant key = keys.entries[i]; | |
| 339 key.writeJsCode(buffer, handler); | |
| 340 buffer.add(": "); | |
| 341 Constant value = values[i]; | |
| 342 value.writeCanonicalizedJsCode(buffer, handler); | |
| 343 } | |
| 344 buffer.add("}"); | |
| 345 } | |
| 346 | |
| 347 void badFieldCountError() { | |
| 348 handler.compiler.internalError( | |
| 349 "Compiler and ConstantMap disagree on number of fields."); | |
| 350 } | |
| 351 | |
| 352 ClassElement classElement = type.element; | |
| 353 buffer.add("new "); | |
| 354 buffer.add(handler.getJsConstructor(classElement)); | |
| 355 buffer.add("("); | |
| 356 // The arguments of the JavaScript constructor for any given Dart class | |
| 357 // are in the same order as the members of the class element. | |
| 358 int emittedArgumentCount = 0; | |
| 359 for (Element element in classElement.members) { | |
| 360 if (element.name == LENGTH_NAME) { | |
| 361 buffer.add(keys.entries.length); | |
| 362 } else if (element.name == JS_OBJECT_NAME) { | |
| 363 writeJsMap(); | |
| 364 } else if (element.name == KEYS_NAME) { | |
| 365 keys.writeCanonicalizedJsCode(buffer, handler); | |
| 366 } else { | |
| 367 // Skip methods. | |
| 368 if (element.kind == ElementKind.FIELD) badFieldCountError(); | |
| 369 continue; | |
| 370 } | |
| 371 emittedArgumentCount++; | |
| 372 if (emittedArgumentCount == 3) { | |
| 373 break; // All arguments have been emitted. | |
| 374 } else { | |
| 375 buffer.add(", "); | |
| 376 } | |
| 377 } | |
| 378 if (emittedArgumentCount != 3) badFieldCountError(); | |
| 379 buffer.add(")"); | |
| 380 } | |
| 381 | |
| 382 bool operator ==(var other) { | |
| 383 if (other is !MapConstant) return false; | |
| 384 MapConstant otherMap = other; | |
| 385 if (hashCode() != otherMap.hashCode()) return false; | |
| 386 // TODO(floitsch): verify that the generic types are the same. | |
| 387 if (keys != otherMap.keys) return false; | |
| 388 for (int i = 0; i < values.length; i++) { | |
| 389 if (values[i] != otherMap.values[i]) return false; | |
| 390 } | |
| 391 return true; | |
| 392 } | |
| 393 | |
| 394 int hashCode() => _hashCode; | |
| 395 | |
| 396 List<Constant> getDependencies() { | |
| 397 List<Constant> result = <Constant>[keys]; | |
| 398 result.addAll(values); | |
| 399 return result; | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 class ConstructedConstant extends ObjectConstant { | |
| 404 final List<Constant> fields; | |
| 405 int _hashCode; | |
| 406 | |
| 407 ConstructedConstant(Type type, this.fields) : super(type) { | |
| 408 assert(type !== null); | |
| 409 // TODO(floitsch): create a better hash. | |
| 410 int hash = 0; | |
| 411 for (Constant field in fields) { | |
| 412 hash ^= field.hashCode(); | |
| 413 } | |
| 414 hash ^= type.element.hashCode(); | |
| 415 _hashCode = hash; | |
| 416 } | |
| 417 bool isConstructedObject() => true; | |
| 418 | |
| 419 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
| 420 buffer.add("new "); | |
| 421 buffer.add(handler.getJsConstructor(type.element)); | |
| 422 buffer.add("("); | |
| 423 for (int i = 0; i < fields.length; i++) { | |
| 424 if (i != 0) buffer.add(", "); | |
| 425 Constant field = fields[i]; | |
| 426 field.writeCanonicalizedJsCode(buffer, handler); | |
| 427 } | |
| 428 buffer.add(")"); | |
| 429 } | |
| 430 | |
| 431 bool operator ==(var otherVar) { | |
| 432 if (otherVar is !ConstructedConstant) return false; | |
| 433 ConstructedConstant other = otherVar; | |
| 434 if (hashCode() != other.hashCode()) return false; | |
| 435 // TODO(floitsch): verify that the (generic) types are the same. | |
| 436 if (type.element != other.type.element) return false; | |
| 437 if (fields.length != other.fields.length) return false; | |
| 438 for (int i = 0; i < fields.length; i++) { | |
| 439 if (fields[i] != other.fields[i]) return false; | |
| 440 } | |
| 441 return true; | |
| 442 } | |
| 443 | |
| 444 int hashCode() => _hashCode; | |
| 445 List<Constant> getDependencies() => fields; | |
| 446 } | |
| 447 | |
| 448 /** | |
| 449 * The [ConstantHandler] keeps track of compile-time constants, | |
| 450 * initializations of global and static fields, and default values of | |
| 451 * optional parameters. | |
| 452 */ | |
| 453 class ConstantHandler extends CompilerTask { | |
| 454 // Contains the initial value of fields. Must contain all static and global | |
| 455 // initializations of used fields. May contain caches for instance fields. | |
| 456 final Map<VariableElement, Constant> initialVariableValues; | |
| 457 | |
| 458 // Map from compile-time constants to their JS name. | |
| 459 final Map<Constant, String> compiledConstants; | |
| 460 | |
| 461 // The set of variable elements that are in the process of being computed. | |
| 462 final Set<VariableElement> pendingVariables; | |
| 463 | |
| 464 ConstantHandler(Compiler compiler) | |
| 465 : initialVariableValues = new Map<VariableElement, Dynamic>(), | |
| 466 compiledConstants = new Map<Constant, String>(), | |
| 467 pendingVariables = new Set<VariableElement>(), | |
| 468 super(compiler); | |
| 469 String get name() => 'ConstantHandler'; | |
| 470 | |
| 471 void registerCompileTimeConstant(Constant constant) { | |
| 472 Function ifAbsentThunk = (() => compiler.namer.getFreshGlobalName("CTC")); | |
| 473 compiledConstants.putIfAbsent(constant, ifAbsentThunk); | |
| 474 } | |
| 475 | |
| 476 /** | |
| 477 * Compiles the initial value of the given field and stores it in an internal | |
| 478 * map. | |
| 479 * | |
| 480 * [WorkItem] must contain a [VariableElement] refering to a global or | |
| 481 * static field. | |
| 482 */ | |
| 483 void compileWorkItem(WorkItem work) { | |
| 484 measure(() { | |
| 485 assert(work.element.kind == ElementKind.FIELD | |
| 486 || work.element.kind == ElementKind.PARAMETER | |
| 487 || work.element.kind == ElementKind.FIELD_PARAMETER); | |
| 488 VariableElement element = work.element; | |
| 489 // Shortcut if it has already been compiled. | |
| 490 if (initialVariableValues.containsKey(element)) return; | |
| 491 compileVariableWithDefinitions(element, work.resolutionTree); | |
| 492 assert(pendingVariables.isEmpty()); | |
| 493 }); | |
| 494 } | |
| 495 | |
| 496 Constant compileVariable(VariableElement element) { | |
| 497 return measure(() { | |
| 498 if (initialVariableValues.containsKey(element)) { | |
| 499 Constant result = initialVariableValues[element]; | |
| 500 return result; | |
| 501 } | |
| 502 TreeElements definitions = compiler.analyzeElement(element); | |
| 503 Constant constant = compileVariableWithDefinitions(element, definitions); | |
| 504 return constant; | |
| 505 }); | |
| 506 } | |
| 507 | |
| 508 Constant compileVariableWithDefinitions(VariableElement element, | |
| 509 TreeElements definitions) { | |
| 510 return measure(() { | |
| 511 Node node = element.parseNode(compiler); | |
| 512 if (pendingVariables.contains(element)) { | |
| 513 MessageKind kind = MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS; | |
| 514 compiler.reportError(node, | |
| 515 new CompileTimeConstantError(kind, const [])); | |
| 516 } | |
| 517 pendingVariables.add(element); | |
| 518 | |
| 519 SendSet assignment = node.asSendSet(); | |
| 520 Constant value; | |
| 521 if (assignment === null) { | |
| 522 // No initial value. | |
| 523 value = new NullConstant(); | |
| 524 } else { | |
| 525 Node right = assignment.arguments.head; | |
| 526 value = compileNodeWithDefinitions(right, definitions); | |
| 527 } | |
| 528 initialVariableValues[element] = value; | |
| 529 pendingVariables.remove(element); | |
| 530 return value; | |
| 531 }); | |
| 532 } | |
| 533 | |
| 534 Constant compileNodeWithDefinitions(Node node, TreeElements definitions) { | |
| 535 return measure(() { | |
| 536 assert(node !== null); | |
| 537 CompileTimeConstantEvaluator evaluator = | |
| 538 new CompileTimeConstantEvaluator(definitions, compiler); | |
| 539 return evaluator.evaluate(node); | |
| 540 }); | |
| 541 } | |
| 542 | |
| 543 /** | |
| 544 * Returns a [List] of static non final fields that need to be initialized. | |
| 545 * The list must be evaluated in order since the fields might depend on each | |
| 546 * other. | |
| 547 */ | |
| 548 List<VariableElement> getStaticNonFinalFieldsForEmission() { | |
| 549 return initialVariableValues.getKeys().filter((element) { | |
| 550 return element.kind == ElementKind.FIELD | |
| 551 && !element.isInstanceMember() | |
| 552 && !element.modifiers.isFinal(); | |
| 553 }); | |
| 554 } | |
| 555 | |
| 556 /** | |
| 557 * Returns a [List] of static final fields that need to be initialized. The | |
| 558 * list must be evaluated in order since the fields might depend on each | |
| 559 * other. | |
| 560 */ | |
| 561 List<VariableElement> getStaticFinalFieldsForEmission() { | |
| 562 return initialVariableValues.getKeys().filter((element) { | |
| 563 return element.kind == ElementKind.FIELD | |
| 564 && !element.isInstanceMember() | |
| 565 && element.modifiers.isFinal(); | |
| 566 }); | |
| 567 } | |
| 568 | |
| 569 List<Constant> getConstantsForEmission() { | |
| 570 // We must emit dependencies before their uses. | |
| 571 Set<Constant> seenConstants = new Set<Constant>(); | |
| 572 List<Constant> result = new List<Constant>(); | |
| 573 | |
| 574 void addConstant(Constant constant) { | |
| 575 if (!seenConstants.contains(constant)) { | |
| 576 constant.getDependencies().forEach(addConstant); | |
| 577 assert(!seenConstants.contains(constant)); | |
| 578 result.add(constant); | |
| 579 seenConstants.add(constant); | |
| 580 } | |
| 581 } | |
| 582 | |
| 583 compiledConstants.forEach((Constant key, ignored) => addConstant(key)); | |
| 584 return result; | |
| 585 } | |
| 586 | |
| 587 String getNameForConstant(Constant constant) { | |
| 588 return compiledConstants[constant]; | |
| 589 } | |
| 590 | |
| 591 StringBuffer writeJsCode(StringBuffer buffer, Constant value) { | |
| 592 value.writeJsCode(buffer, this); | |
| 593 return buffer; | |
| 594 } | |
| 595 | |
| 596 StringBuffer writeJsCodeForVariable(StringBuffer buffer, | |
| 597 VariableElement element) { | |
| 598 if (!initialVariableValues.containsKey(element)) { | |
| 599 compiler.internalError("No initial value for given element", | |
| 600 element: element); | |
| 601 } | |
| 602 Constant constant = initialVariableValues[element]; | |
| 603 if (constant.isObject()) { | |
| 604 String name = compiledConstants[constant]; | |
| 605 buffer.add("${compiler.namer.ISOLATE}.prototype.$name"); | |
| 606 } else { | |
| 607 writeJsCode(buffer, constant); | |
| 608 } | |
| 609 return buffer; | |
| 610 } | |
| 611 | |
| 612 /** | |
| 613 * Write the contents of the quoted string to a [StringBuffer] in | |
| 614 * a form that is valid as JavaScript string literal content. | |
| 615 * The string is assumed quoted by single quote characters. | |
| 616 */ | |
| 617 static void writeEscapedString(DartString string, | |
| 618 StringBuffer buffer, | |
| 619 void cancel(String reason)) { | |
| 620 Iterator<int> iterator = string.iterator(); | |
| 621 while (iterator.hasNext()) { | |
| 622 int code = iterator.next(); | |
| 623 if (code === $SQ) { | |
| 624 buffer.add(@"\'"); | |
| 625 } else if (code === $LF) { | |
| 626 buffer.add(@'\n'); | |
| 627 } else if (code === $CR) { | |
| 628 buffer.add(@'\r'); | |
| 629 } else if (code === $LS) { | |
| 630 // This Unicode line terminator and $PS are invalid in JS string | |
| 631 // literals. | |
| 632 buffer.add(@'\u2028'); | |
| 633 } else if (code === $PS) { | |
| 634 buffer.add(@'\u2029'); | |
| 635 } else if (code === $BACKSLASH) { | |
| 636 buffer.add(@'\\'); | |
| 637 } else { | |
| 638 if (code > 0xffff) { | |
| 639 cancel('Unhandled non-BMP character: U+${code.toRadixString(16)}'); | |
| 640 } | |
| 641 // TODO(lrn): Consider whether all codes above 0x7f really need to | |
| 642 // be escaped. We build a Dart string here, so it should be a literal | |
| 643 // stage that converts it to, e.g., UTF-8 for a JS interpreter. | |
| 644 if (code < 0x20) { | |
| 645 buffer.add(@'\x'); | |
| 646 if (code < 0x10) buffer.add('0'); | |
| 647 buffer.add(code.toRadixString(16)); | |
| 648 } else if (code >= 0x80) { | |
| 649 if (code < 0x100) { | |
| 650 buffer.add(@'\x'); | |
| 651 buffer.add(code.toRadixString(16)); | |
| 652 } else { | |
| 653 buffer.add(@'\u'); | |
| 654 if (code < 0x1000) { | |
| 655 buffer.add('0'); | |
| 656 } | |
| 657 buffer.add(code.toRadixString(16)); | |
| 658 } | |
| 659 } else { | |
| 660 buffer.add(new String.fromCharCodes(<int>[code])); | |
| 661 } | |
| 662 } | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 String getJsConstructor(ClassElement element) { | |
| 667 return compiler.namer.isolatePropertyAccess(element); | |
| 668 } | |
| 669 } | |
| 670 | |
| 671 class CompileTimeConstantEvaluator extends AbstractVisitor { | |
| 672 final TreeElements elements; | |
| 673 final Compiler compiler; | |
| 674 | |
| 675 CompileTimeConstantEvaluator(this.elements, this.compiler); | |
| 676 | |
| 677 Constant evaluate(Node node) { | |
| 678 return node.accept(this); | |
| 679 } | |
| 680 | |
| 681 visitNode(Node node) { | |
| 682 error(node); | |
| 683 } | |
| 684 | |
| 685 Constant visitLiteralBool(LiteralBool node) { | |
| 686 return new BoolConstant(node.value); | |
| 687 } | |
| 688 | |
| 689 Constant visitLiteralDouble(LiteralDouble node) { | |
| 690 return new DoubleConstant(node.value); | |
| 691 } | |
| 692 | |
| 693 Constant visitLiteralInt(LiteralInt node) { | |
| 694 return new IntConstant(node.value); | |
| 695 } | |
| 696 | |
| 697 Constant visitLiteralList(LiteralList node) { | |
| 698 if (!node.isConst()) error(node); | |
| 699 List<Constant> arguments = <Constant>[]; | |
| 700 for (Link<Node> link = node.elements.nodes; | |
| 701 !link.isEmpty(); | |
| 702 link = link.tail) { | |
| 703 arguments.add(evaluate(link.head)); | |
| 704 } | |
| 705 // TODO(floitsch): get type from somewhere. | |
| 706 Type type = null; | |
| 707 Constant constant = new ListConstant(type, arguments); | |
| 708 compiler.constantHandler.registerCompileTimeConstant(constant); | |
| 709 return constant; | |
| 710 } | |
| 711 | |
| 712 Constant visitLiteralMap(LiteralMap node) { | |
| 713 // TODO(floitsch): check for isConst, once the parser adds it into the node. | |
| 714 // if (!node.isConst()) error(node); | |
| 715 List<StringConstant> keys = <StringConstant>[]; | |
| 716 List<Constant> values = <Constant>[]; | |
| 717 bool hasProtoKey = false; | |
| 718 for (Link<Node> link = node.entries.nodes; | |
| 719 !link.isEmpty(); | |
| 720 link = link.tail) { | |
| 721 LiteralMapEntry entry = link.head; | |
| 722 Constant key = evaluate(entry.key); | |
| 723 if (!key.isString() || entry.key.asLiteralString() === null) { | |
| 724 MessageKind kind = MessageKind.KEY_NOT_A_STRING_LITERAL; | |
| 725 compiler.reportError(entry.key, new ResolutionError(kind, const [])); | |
| 726 } | |
| 727 // TODO(floitsch): make this faster. | |
| 728 StringConstant keyConstant = key; | |
| 729 if (keyConstant.value == new LiteralDartString("__proto__")) { | |
| 730 hasProtoKey = true; | |
| 731 } | |
| 732 keys.add(key); | |
| 733 values.add(evaluate(entry.value)); | |
| 734 } | |
| 735 if (hasProtoKey) { | |
| 736 compiler.unimplemented("visitLiteralMap with __proto__ key", | |
| 737 node: node); | |
| 738 } | |
| 739 // TODO(floitsch): this should be a List<String> type. | |
| 740 Type keysType = null; | |
| 741 ListConstant keysList = new ListConstant(keysType, keys); | |
| 742 compiler.constantHandler.registerCompileTimeConstant(keysList); | |
| 743 ClassElement classElement = | |
| 744 compiler.jsHelperLibrary.find(MapConstant.DART_CLASS); | |
| 745 classElement.ensureResolved(compiler); | |
| 746 // TODO(floitsch): copy over the generic type. | |
| 747 Type type = new SimpleType(classElement.name, classElement); | |
| 748 compiler.registerInstantiatedClass(classElement); | |
| 749 Constant constant = new MapConstant(type, keysList, values); | |
| 750 compiler.constantHandler.registerCompileTimeConstant(constant); | |
| 751 return constant; | |
| 752 } | |
| 753 | |
| 754 Constant visitLiteralNull(LiteralNull node) { | |
| 755 return new NullConstant(); | |
| 756 } | |
| 757 | |
| 758 Constant visitLiteralString(LiteralString node) { | |
| 759 return new StringConstant(node.dartString); | |
| 760 } | |
| 761 | |
| 762 Constant visitStringJuxtaposition(StringJuxtaposition node) { | |
| 763 StringConstant left = evaluate(node.first); | |
| 764 StringConstant right = evaluate(node.second); | |
| 765 return new StringConstant(new DartString.concat(left.value, right.value)); | |
| 766 } | |
| 767 | |
| 768 Constant visitStringInterpolation(StringInterpolation node) { | |
| 769 StringConstant initialString = evaluate(node.string); | |
| 770 DartString accumulator = initialString.value; | |
| 771 for (StringInterpolationPart part in node.parts) { | |
| 772 Constant expression = evaluate(part.expression); | |
| 773 DartString expressionString; | |
| 774 if (expression.isNum() || expression.isBool()) { | |
| 775 Object value = expression.value; | |
| 776 expressionString = new DartString.literal(value.toString()); | |
| 777 } else if (expression.isString()) { | |
| 778 expressionString = expression.value; | |
| 779 } else { | |
| 780 error(part.expression); | |
| 781 } | |
| 782 accumulator = new DartString.concat(accumulator, expressionString); | |
| 783 StringConstant partString = evaluate(part.string); | |
| 784 accumulator = new DartString.concat(accumulator, partString.value); | |
| 785 }; | |
| 786 return new StringConstant(accumulator); | |
| 787 } | |
| 788 | |
| 789 // TODO(floitsch): provide better error-messages. | |
| 790 Constant visitSend(Send send) { | |
| 791 Element element = elements[send]; | |
| 792 if (Elements.isStaticOrTopLevelField(element)) { | |
| 793 if (element.modifiers === null || | |
| 794 !element.modifiers.isFinal()) { | |
| 795 error(send); | |
| 796 } | |
| 797 return compiler.compileVariable(element); | |
| 798 } else if (send.isPrefix) { | |
| 799 assert(send.isOperator); | |
| 800 Constant receiverConstant = evaluate(send.receiver); | |
| 801 Operator op = send.selector; | |
| 802 Constant folded; | |
| 803 switch (op.source.stringValue) { | |
| 804 case "!": | |
| 805 folded = const NotOperation().fold(receiverConstant); | |
| 806 break; | |
| 807 case "-": | |
| 808 folded = const NegateOperation().fold(receiverConstant); | |
| 809 break; | |
| 810 case "~": | |
| 811 folded = const BitNotOperation().fold(receiverConstant); | |
| 812 break; | |
| 813 default: | |
| 814 compiler.internalError("Unexpected operator.", node: op); | |
| 815 break; | |
| 816 } | |
| 817 if (folded === null) error(send); | |
| 818 return folded; | |
| 819 } else if (send.isOperator && !send.isPostfix) { | |
| 820 assert(send.argumentCount() == 1); | |
| 821 Constant left = evaluate(send.receiver); | |
| 822 Constant right = evaluate(send.argumentsNode.nodes.head); | |
| 823 Operator op = send.selector.asOperator(); | |
| 824 Constant folded; | |
| 825 switch (op.source.stringValue) { | |
| 826 case "+": | |
| 827 if (left.isString() && !right.isString()) { | |
| 828 // At the moment only compile-time concatenation of two strings is | |
| 829 // allowed. | |
| 830 error(send); | |
| 831 } | |
| 832 folded = const AddOperation().fold(left, right); | |
| 833 break; | |
| 834 case "-": | |
| 835 folded = const SubtractOperation().fold(left, right); | |
| 836 break; | |
| 837 case "*": | |
| 838 folded = const MultiplyOperation().fold(left, right); | |
| 839 break; | |
| 840 case "/": | |
| 841 folded = const DivideOperation().fold(left, right); | |
| 842 break; | |
| 843 case "%": | |
| 844 folded = const ModuloOperation().fold(left, right); | |
| 845 break; | |
| 846 case "~/": | |
| 847 folded = const TruncatingDivideOperation().fold(left, right); | |
| 848 break; | |
| 849 case "|": | |
| 850 folded = const BitOrOperation().fold(left, right); | |
| 851 break; | |
| 852 case "&": | |
| 853 folded = const BitAndOperation().fold(left, right); | |
| 854 break; | |
| 855 case "^": | |
| 856 folded = const BitXorOperation().fold(left, right); | |
| 857 break; | |
| 858 case "||": | |
| 859 folded = const BooleanOr().fold(left, right); | |
| 860 break; | |
| 861 case "&&": | |
| 862 folded = const BooleanAnd().fold(left, right); | |
| 863 break; | |
| 864 case "<<": | |
| 865 folded = const ShiftLeftOperation().fold(left, right); | |
| 866 break; | |
| 867 case ">>": | |
| 868 folded = const ShiftRightOperation().fold(left, right); | |
| 869 break; | |
| 870 case "<": | |
| 871 folded = const LessOperation().fold(left, right); | |
| 872 break; | |
| 873 case "<=": | |
| 874 folded = const LessEqualOperation().fold(left, right); | |
| 875 break; | |
| 876 case ">": | |
| 877 folded = const GreaterOperation().fold(left, right); | |
| 878 break; | |
| 879 case ">=": | |
| 880 folded = const GreaterEqualOperation().fold(left, right); | |
| 881 break; | |
| 882 case "==": | |
| 883 if (left.isPrimitive() && right.isPrimitive()) { | |
| 884 folded = const EqualsOperation().fold(left, right); | |
| 885 } | |
| 886 break; | |
| 887 case "===": | |
| 888 if (left.isPrimitive() && right.isPrimitive()) { | |
| 889 folded = const IdentityOperation().fold(left, right); | |
| 890 } | |
| 891 break; | |
| 892 case "!=": | |
| 893 if (left.isPrimitive() && right.isPrimitive()) { | |
| 894 BoolConstant areEquals = const EqualsOperation().fold(left, right); | |
| 895 if (areEquals === null) { | |
| 896 folded = null; | |
| 897 } else { | |
| 898 folded = areEquals.negate(); | |
| 899 } | |
| 900 } | |
| 901 break; | |
| 902 case "!==": | |
| 903 if (left.isPrimitive() && right.isPrimitive()) { | |
| 904 BoolConstant areIdentical = | |
| 905 const IdentityOperation().fold(left, right); | |
| 906 if (areIdentical === null) { | |
| 907 folded = null; | |
| 908 } else { | |
| 909 folded = areIdentical.negate(); | |
| 910 } | |
| 911 } | |
| 912 break; | |
| 913 default: | |
| 914 compiler.internalError("Unexpected operator.", node: op); | |
| 915 break; | |
| 916 } | |
| 917 if (folded === null) error(send); | |
| 918 return folded; | |
| 919 } | |
| 920 return super.visitSend(send); | |
| 921 } | |
| 922 | |
| 923 visitSendSet(SendSet node) { | |
| 924 error(node); | |
| 925 } | |
| 926 | |
| 927 /** Returns the list of constants that are passed to the static function. */ | |
| 928 List<Constant> evaluateArgumentsToConstructor(Send send, | |
| 929 FunctionElement target) { | |
| 930 FunctionParameters parameters = target.computeParameters(compiler); | |
| 931 List<Constant> arguments = <Constant>[]; | |
| 932 Selector selector = elements.getSelector(send); | |
| 933 | |
| 934 Function compileArgument = evaluate; | |
| 935 Function compileConstant = compiler.compileVariable; | |
| 936 bool succeeded = selector.addSendArgumentsToList( | |
| 937 send, arguments, parameters, compileArgument, compileConstant); | |
| 938 if (!succeeded) error(send); | |
| 939 return arguments; | |
| 940 } | |
| 941 | |
| 942 Constant visitNewExpression(NewExpression node) { | |
| 943 if (!node.isConst()) error(node); | |
| 944 | |
| 945 FunctionElement constructor = elements[node.send]; | |
| 946 ClassElement classElement = constructor.enclosingElement; | |
| 947 if (classElement.isInterface()) { | |
| 948 compiler.resolver.resolveMethodElement(constructor); | |
| 949 constructor = constructor.defaultImplementation; | |
| 950 classElement = constructor.enclosingElement; | |
| 951 } | |
| 952 | |
| 953 List<Constant> arguments = | |
| 954 evaluateArgumentsToConstructor(node.send, constructor); | |
| 955 ConstructorEvaluator evaluator = | |
| 956 new ConstructorEvaluator(constructor, compiler); | |
| 957 evaluator.evaluateConstructorFieldValues(arguments); | |
| 958 List<Constant>jsNewArguments = evaluator.buildJsNewArguments(classElement); | |
| 959 | |
| 960 compiler.registerInstantiatedClass(classElement); | |
| 961 // TODO(floitsch): take generic types into account. | |
| 962 Type type = classElement.computeType(compiler); | |
| 963 Constant constant = new ConstructedConstant(type, jsNewArguments); | |
| 964 compiler.constantHandler.registerCompileTimeConstant(constant); | |
| 965 return constant; | |
| 966 } | |
| 967 | |
| 968 Constant visitParenthesizedExpression(ParenthesizedExpression node) { | |
| 969 return node.expression.accept(this); | |
| 970 } | |
| 971 | |
| 972 error(Node node) { | |
| 973 // TODO(floitsch): get the list of constants that are currently compiled | |
| 974 // and present some kind of stack-trace. | |
| 975 MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT; | |
| 976 compiler.reportError(node, new CompileTimeConstantError(kind, const [])); | |
| 977 } | |
| 978 } | |
| 979 | |
| 980 class ConstructorEvaluator extends CompileTimeConstantEvaluator { | |
| 981 FunctionElement constructor; | |
| 982 final Map<Element, Constant> definitions; | |
| 983 final Map<Element, Constant> fieldValues; | |
| 984 | |
| 985 ConstructorEvaluator(FunctionElement constructor, Compiler compiler) | |
| 986 : this.constructor = constructor, | |
| 987 this.definitions = new Map<Element, Constant>(), | |
| 988 this.fieldValues = new Map<Element, Constant>(), | |
| 989 super(compiler.resolver.resolveMethodElement(constructor), | |
| 990 compiler); | |
| 991 | |
| 992 Constant visitSend(Send send) { | |
| 993 Element element = elements[send]; | |
| 994 if (Elements.isLocal(element)) { | |
| 995 Constant constant = definitions[element]; | |
| 996 if (constant === null) { | |
| 997 compiler.internalError("Local variable without value", node: send); | |
| 998 } | |
| 999 return constant; | |
| 1000 } | |
| 1001 return super.visitSend(send); | |
| 1002 } | |
| 1003 | |
| 1004 /** | |
| 1005 * Given the arguments (a list of constants) assigns them to the parameters, | |
| 1006 * updating the definitions map. If the constructor has field-initializer | |
| 1007 * parameters (like [:this.x:]), also updates the [fieldValues] map. | |
| 1008 */ | |
| 1009 void assignArgumentsToParameters(List<Constant> arguments) { | |
| 1010 // Assign arguments to parameters. | |
| 1011 FunctionParameters parameters = constructor.computeParameters(compiler); | |
| 1012 int index = 0; | |
| 1013 parameters.forEachParameter((Element parameter) { | |
| 1014 Constant argument = arguments[index++]; | |
| 1015 definitions[parameter] = argument; | |
| 1016 if (parameter.kind == ElementKind.FIELD_PARAMETER) { | |
| 1017 FieldParameterElement fieldParameterElement = parameter; | |
| 1018 fieldValues[fieldParameterElement.fieldElement] = argument; | |
| 1019 } | |
| 1020 }); | |
| 1021 } | |
| 1022 | |
| 1023 void evaluateSuperOrRedirectSend(FunctionElement targetConstructor, | |
| 1024 List<Constant> targetArguments) { | |
| 1025 ConstructorEvaluator evaluator = | |
| 1026 new ConstructorEvaluator(targetConstructor, compiler); | |
| 1027 evaluator.evaluateConstructorFieldValues(targetArguments); | |
| 1028 // Copy over the fieldValues from the super/redirect-constructor. | |
| 1029 evaluator.fieldValues.forEach((key, value) => fieldValues[key] = value); | |
| 1030 } | |
| 1031 | |
| 1032 /** | |
| 1033 * Runs through the initializers of the given [constructor] and updates | |
| 1034 * the [fieldValues] map. | |
| 1035 */ | |
| 1036 void evaluateConstructorInitializers() { | |
| 1037 FunctionExpression functionNode = constructor.parseNode(compiler); | |
| 1038 NodeList initializerList = functionNode.initializers; | |
| 1039 | |
| 1040 bool foundSuperOrRedirect = false; | |
| 1041 | |
| 1042 if (initializerList !== null) { | |
| 1043 for (Link<Node> link = initializerList.nodes; | |
| 1044 !link.isEmpty(); | |
| 1045 link = link.tail) { | |
| 1046 assert(link.head is Send); | |
| 1047 if (link.head is !SendSet) { | |
| 1048 // A super initializer or constructor redirection. | |
| 1049 Send call = link.head; | |
| 1050 FunctionElement targetConstructor = elements[call]; | |
| 1051 List<Constant> targetArguments = | |
| 1052 evaluateArgumentsToConstructor(call, targetConstructor); | |
| 1053 evaluateSuperOrRedirectSend(targetConstructor, targetArguments); | |
| 1054 foundSuperOrRedirect = true; | |
| 1055 } else { | |
| 1056 // A field initializer. | |
| 1057 SendSet init = link.head; | |
| 1058 Link<Node> initArguments = init.arguments; | |
| 1059 assert(!initArguments.isEmpty() && initArguments.tail.isEmpty()); | |
| 1060 Constant fieldValue = evaluate(initArguments.head); | |
| 1061 fieldValues[elements[init]] = fieldValue; | |
| 1062 } | |
| 1063 } | |
| 1064 } | |
| 1065 | |
| 1066 if (!foundSuperOrRedirect) { | |
| 1067 // No super initializer found. Try to find the default constructor if | |
| 1068 // the class is not Object. | |
| 1069 ClassElement enclosingClass = constructor.enclosingElement; | |
| 1070 ClassElement superClass = enclosingClass.superclass; | |
| 1071 if (enclosingClass != compiler.objectClass) { | |
| 1072 assert(superClass !== null); | |
| 1073 assert(superClass.isResolved); | |
| 1074 FunctionElement targetConstructor = | |
| 1075 superClass.lookupConstructor(superClass.name); | |
| 1076 if (targetConstructor === null) { | |
| 1077 compiler.internalError("no default constructor available"); | |
| 1078 } | |
| 1079 evaluateSuperOrRedirectSend(targetConstructor, const <Constant>[]); | |
| 1080 } | |
| 1081 } | |
| 1082 } | |
| 1083 | |
| 1084 /** | |
| 1085 * Simulates the execution of the [constructor] with the given | |
| 1086 * [arguments] to obtain the field values that need to be passed to the | |
| 1087 * native JavaScript constructor. | |
| 1088 */ | |
| 1089 void evaluateConstructorFieldValues(List<Constant> arguments) { | |
| 1090 compiler.withCurrentElement(constructor, () { | |
| 1091 assignArgumentsToParameters(arguments); | |
| 1092 evaluateConstructorInitializers(); | |
| 1093 }); | |
| 1094 } | |
| 1095 | |
| 1096 List<Constant> buildJsNewArguments(ClassElement classElement) { | |
| 1097 List<Constant> jsNewArguments = <Constant>[]; | |
| 1098 // TODO(floitsch): share this code with the emitter, so that we don't | |
| 1099 // need to care about the order of fields here. | |
| 1100 while (classElement != compiler.objectClass) { | |
| 1101 for (Element member in classElement.members) { | |
| 1102 if (member.isInstanceMember() && member.kind == ElementKind.FIELD) { | |
| 1103 Constant fieldValue = fieldValues[member]; | |
| 1104 if (fieldValue === null) { | |
| 1105 // Use the default value. | |
| 1106 fieldValue = compiler.compileVariable(member); | |
| 1107 } | |
| 1108 jsNewArguments.add(fieldValue); | |
| 1109 } | |
| 1110 } | |
| 1111 classElement = classElement.superclass; | |
| 1112 } | |
| 1113 return jsNewArguments; | |
| 1114 } | |
| 1115 } | |
| OLD | NEW |