OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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 class Constant implements Hashable { | 5 class Constant implements Hashable { |
6 const Constant(); | 6 const Constant(); |
7 | 7 |
8 bool isNull() => false; | 8 bool isNull() => false; |
9 bool isBool() => false; | 9 bool isBool() => false; |
10 bool isTrue() => false; | 10 bool isTrue() => false; |
11 bool isFalse() => false; | 11 bool isFalse() => false; |
12 bool isInt() => false; | 12 bool isInt() => false; |
13 bool isDouble() => false; | 13 bool isDouble() => false; |
14 bool isNum() => false; | 14 bool isNum() => false; |
15 bool isString() => false; | 15 bool isString() => false; |
16 bool isList() => false; | 16 bool isList() => false; |
17 bool isMap() => false; | 17 bool isMap() => false; |
18 bool isConstructedObject() => false; | 18 bool isConstructedObject() => false; |
19 /** Returns true if the constant is a list, a map or a constructed object. */ | 19 /** Returns true if the constant is a list, a map or a constructed object. */ |
20 bool isObject() => false; | 20 bool isObject() => false; |
21 | 21 |
22 abstract void writeJsCode(StringBuffer buffer, ConstantHandler handler); | 22 abstract void writeJsCode(StringBuffer buffer, ConstantHandler handler); |
| 23 abstract List<Constant> getDependencies(); |
23 } | 24 } |
24 | 25 |
25 class PrimitiveConstant extends Constant { | 26 class PrimitiveConstant extends Constant { |
26 abstract get value(); | 27 abstract get value(); |
27 const PrimitiveConstant(); | 28 const PrimitiveConstant(); |
28 | 29 |
29 bool operator ==(var other) { | 30 bool operator ==(var other) { |
30 if (other is !PrimitiveConstant) return false; | 31 if (other is !PrimitiveConstant) return false; |
31 PrimitiveConstant otherPrimitive = other; | 32 PrimitiveConstant otherPrimitive = other; |
32 // We use == instead of === so that DartStrings compare correctly. | 33 // We use == instead of === so that DartStrings compare correctly. |
33 return value == otherPrimitive.value; | 34 return value == otherPrimitive.value; |
34 } | 35 } |
35 | 36 |
36 String toString() => value.toString(); | 37 String toString() => value.toString(); |
| 38 // Primitive constants don't have dependencies. |
| 39 List<Constant> getDependencies() => const <Constant>[]; |
37 abstract DartString toDartString(); | 40 abstract DartString toDartString(); |
38 } | 41 } |
39 | 42 |
40 class NullConstant extends PrimitiveConstant { | 43 class NullConstant extends PrimitiveConstant { |
41 factory NullConstant() => const NullConstant._internal(); | 44 factory NullConstant() => const NullConstant._internal(); |
42 const NullConstant._internal(); | 45 const NullConstant._internal(); |
43 bool isNull() => true; | 46 bool isNull() => true; |
44 get value() => null; | 47 get value() => null; |
45 | 48 |
46 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | 49 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 entry.writeJsCode(buffer, handler); | 271 entry.writeJsCode(buffer, handler); |
269 } | 272 } |
270 } | 273 } |
271 buffer.add("])"); | 274 buffer.add("])"); |
272 } | 275 } |
273 | 276 |
274 bool operator ==(var other) { | 277 bool operator ==(var other) { |
275 if (other is !ListConstant) return false; | 278 if (other is !ListConstant) return false; |
276 ListConstant otherList = other; | 279 ListConstant otherList = other; |
277 if (hashCode() != otherList.hashCode()) return false; | 280 if (hashCode() != otherList.hashCode()) return false; |
278 // TODO(floitsch): verify that the types are the same. | 281 // TODO(floitsch): verify that the generic types are the same. |
279 if (entries.length != otherList.entries.length) return false; | 282 if (entries.length != otherList.entries.length) return false; |
280 for (int i = 0; i < entries.length; i++) { | 283 for (int i = 0; i < entries.length; i++) { |
281 if (entries[i] != otherList.entries[i]) return false; | 284 if (entries[i] != otherList.entries[i]) return false; |
282 } | 285 } |
283 return true; | 286 return true; |
284 } | 287 } |
285 | 288 |
286 int hashCode() => _hashCode; | 289 int hashCode() => _hashCode; |
| 290 |
| 291 List<Constant> getDependencies() => entries; |
| 292 } |
| 293 |
| 294 class MapConstant extends ObjectConstant { |
| 295 /** The dart class implementing constant map literals. */ |
| 296 static final SourceString DART_CLASS = const SourceString("ConstantMap"); |
| 297 static final SourceString LENGTH_NAME = const SourceString("length"); |
| 298 static final SourceString JS_OBJECT_NAME = const SourceString("_jsObject"); |
| 299 static final SourceString KEYS_NAME = const SourceString("_keys"); |
| 300 |
| 301 final ListConstant keys; |
| 302 final List<Constant> values; |
| 303 int _hashCode; |
| 304 |
| 305 MapConstant(Type type, this.keys, this.values) : super(type) { |
| 306 // TODO(floitsch): create a better hash. |
| 307 int hash = 0; |
| 308 for (Constant value in values) hash ^= value.hashCode(); |
| 309 _hashCode = hash; |
| 310 } |
| 311 bool isMap() => true; |
| 312 |
| 313 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { |
| 314 String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype"; |
| 315 |
| 316 void writeJsMap() { |
| 317 buffer.add("{"); |
| 318 for (int i = 0; i < keys.entries.length; i++) { |
| 319 if (i != 0) buffer.add(", "); |
| 320 |
| 321 StringConstant key = keys.entries[i]; |
| 322 key.writeJsCode(buffer, handler); |
| 323 buffer.add(": "); |
| 324 Constant value = values[i]; |
| 325 // TODO(floitsch): share this code with the ListConstant and |
| 326 // ConstructedConstant. |
| 327 if (value.isObject()) { |
| 328 String name = handler.getNameForConstant(value); |
| 329 buffer.add("$isolatePrototype.$name"); |
| 330 } else { |
| 331 value.writeJsCode(buffer, handler); |
| 332 } |
| 333 } |
| 334 buffer.add("}"); |
| 335 } |
| 336 |
| 337 void badFieldCountError() { |
| 338 handler.compiler.internalError( |
| 339 "Compiler and ConstantMap disagree on number of fields."); |
| 340 } |
| 341 |
| 342 ClassElement classElement = type.element; |
| 343 buffer.add("new "); |
| 344 buffer.add(handler.getJsConstructor(classElement)); |
| 345 buffer.add("("); |
| 346 // The arguments of the JavaScript constructor for any given Dart class |
| 347 // are in the same order as the members of the class element. |
| 348 int emittedArgumentCount = 0; |
| 349 for (Element element in classElement.members) { |
| 350 if (element.name == LENGTH_NAME) { |
| 351 buffer.add(keys.entries.length); |
| 352 } else if (element.name == JS_OBJECT_NAME) { |
| 353 writeJsMap(); |
| 354 } else if (element.name == KEYS_NAME) { |
| 355 // TODO(floitsch): share this code with the ListConstant and |
| 356 // ConstructedConstant. |
| 357 String name = handler.getNameForConstant(keys); |
| 358 buffer.add("$isolatePrototype.$name"); |
| 359 } else { |
| 360 // Skip methods. |
| 361 if (element.kind == ElementKind.FIELD) badFieldCountError(); |
| 362 continue; |
| 363 } |
| 364 emittedArgumentCount++; |
| 365 if (emittedArgumentCount == 3) { |
| 366 break; // All arguments have been emitted. |
| 367 } else { |
| 368 buffer.add(", "); |
| 369 } |
| 370 } |
| 371 if (emittedArgumentCount != 3) badFieldCountError(); |
| 372 buffer.add(")"); |
| 373 } |
| 374 |
| 375 bool operator ==(var other) { |
| 376 if (other is !MapConstant) return false; |
| 377 MapConstant otherMap = other; |
| 378 if (hashCode() != otherMap.hashCode()) return false; |
| 379 // TODO(floitsch): verify that the generic types are the same. |
| 380 if (keys != otherMap.keys) return false; |
| 381 for (int i = 0; i < values.length; i++) { |
| 382 if (values[i] != otherMap.values[i]) return false; |
| 383 } |
| 384 return true; |
| 385 } |
| 386 |
| 387 int hashCode() => _hashCode; |
| 388 |
| 389 List<Constant> getDependencies() { |
| 390 List<Constant> result = <Constant>[keys]; |
| 391 result.addAll(values); |
| 392 return result; |
| 393 } |
287 } | 394 } |
288 | 395 |
289 class ConstructedConstant extends ObjectConstant { | 396 class ConstructedConstant extends ObjectConstant { |
290 final List<Constant> fields; | 397 final List<Constant> fields; |
291 int _hashCode; | 398 int _hashCode; |
292 | 399 |
293 ConstructedConstant(Type type, this.fields) : super(type) { | 400 ConstructedConstant(Type type, this.fields) : super(type) { |
294 assert(type !== null); | 401 assert(type !== null); |
295 // TODO(floitsch): create a better hash. | 402 // TODO(floitsch): create a better hash. |
296 int hash = 0; | 403 int hash = 0; |
297 for (Constant field in fields) { | 404 for (Constant field in fields) { |
298 hash ^= field.hashCode(); | 405 hash ^= field.hashCode(); |
299 } | 406 } |
300 hash ^= type.element.hashCode(); | 407 hash ^= type.element.hashCode(); |
301 _hashCode = hash; | 408 _hashCode = hash; |
302 } | 409 } |
303 bool isConstructedObject() => true; | 410 bool isConstructedObject() => true; |
304 | 411 |
305 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | 412 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { |
306 buffer.add("new "); | 413 buffer.add("new "); |
307 buffer.add(handler.getJsConstructor(type.element)); | 414 buffer.add(handler.getJsConstructor(type.element)); |
308 buffer.add("("); | 415 buffer.add("("); |
309 String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype"; | 416 String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype"; |
310 for (int i = 0; i < fields.length; i++) { | 417 for (int i = 0; i < fields.length; i++) { |
311 if (i != 0) buffer.add(", "); | 418 if (i != 0) buffer.add(", "); |
312 Constant field = fields[i]; | 419 Constant field = fields[i]; |
313 // TODO(floitsch): share this code with the ListConstant. | 420 // TODO(floitsch): share this code with the ListConstant. |
314 if (field.isObject()) { | 421 if (field.isObject()) { |
315 String name = handler.getNameForConstant(entry); | 422 String name = handler.getNameForConstant(field); |
316 buffer.add("$isolatePrototype.$name"); | 423 buffer.add("$isolatePrototype.$name"); |
317 } else { | 424 } else { |
318 field.writeJsCode(buffer, handler); | 425 field.writeJsCode(buffer, handler); |
319 } | 426 } |
320 } | 427 } |
321 buffer.add(")"); | 428 buffer.add(")"); |
322 } | 429 } |
323 | 430 |
324 bool operator ==(var otherVar) { | 431 bool operator ==(var otherVar) { |
325 if (otherVar is !ConstructedConstant) return false; | 432 if (otherVar is !ConstructedConstant) return false; |
326 ConstructedConstant other = otherVar; | 433 ConstructedConstant other = otherVar; |
327 if (hashCode() != other.hashCode()) return false; | 434 if (hashCode() != other.hashCode()) return false; |
328 // TODO(floitsch): verify that the (generic) types are the same. | 435 // TODO(floitsch): verify that the (generic) types are the same. |
329 if (type.element != other.type.element) return false; | 436 if (type.element != other.type.element) return false; |
330 if (fields.length != other.fields.length) return false; | 437 if (fields.length != other.fields.length) return false; |
331 for (int i = 0; i < fields.length; i++) { | 438 for (int i = 0; i < fields.length; i++) { |
332 if (fields[i] != other.fields[i]) return false; | 439 if (fields[i] != other.fields[i]) return false; |
333 } | 440 } |
334 return true; | 441 return true; |
335 } | 442 } |
336 | 443 |
337 int hashCode() => _hashCode; | 444 int hashCode() => _hashCode; |
| 445 List<Constant> getDependencies() => fields; |
338 } | 446 } |
339 | 447 |
340 /** | 448 /** |
341 * The [ConstantHandler] keeps track of compile-time constants, | 449 * The [ConstantHandler] keeps track of compile-time constants, |
342 * initializations of global and static fields, and default values of | 450 * initializations of global and static fields, and default values of |
343 * optional parameters. | 451 * optional parameters. |
344 */ | 452 */ |
345 class ConstantHandler extends CompilerTask { | 453 class ConstantHandler extends CompilerTask { |
346 // Contains the initial value of fields. Must contain all static and global | 454 // Contains the initial value of fields. Must contain all static and global |
347 // initializations of used fields. May contain caches for instance fields. | 455 // initializations of used fields. May contain caches for instance fields. |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
459 */ | 567 */ |
460 List<VariableElement> getStaticFinalFieldsForEmission() { | 568 List<VariableElement> getStaticFinalFieldsForEmission() { |
461 return initialVariableValues.getKeys().filter((element) { | 569 return initialVariableValues.getKeys().filter((element) { |
462 return element.kind == ElementKind.FIELD | 570 return element.kind == ElementKind.FIELD |
463 && !element.isInstanceMember() | 571 && !element.isInstanceMember() |
464 && element.modifiers.isFinal(); | 572 && element.modifiers.isFinal(); |
465 }); | 573 }); |
466 } | 574 } |
467 | 575 |
468 List<Constant> getConstantsForEmission() { | 576 List<Constant> getConstantsForEmission() { |
469 return compiledConstants.getKeys(); | 577 // We must emit dependencies before their uses. |
| 578 Set<Constant> seenConstants = new Set<Constant>(); |
| 579 List<Constant> result = new List<Constant>(); |
| 580 |
| 581 void addConstant(Constant constant) { |
| 582 if (!seenConstants.contains(constant)) { |
| 583 constant.getDependencies().forEach(addConstant); |
| 584 assert(!seenConstants.contains(constant)); |
| 585 result.add(constant); |
| 586 seenConstants.add(constant); |
| 587 } |
| 588 } |
| 589 |
| 590 compiledConstants.forEach((Constant key, ignored) => addConstant(key)); |
| 591 return result; |
470 } | 592 } |
471 | 593 |
472 String getNameForConstant(Constant constant) { | 594 String getNameForConstant(Constant constant) { |
473 return compiledConstants[constant]; | 595 return compiledConstants[constant]; |
474 } | 596 } |
475 | 597 |
476 StringBuffer writeJsCode(StringBuffer buffer, Constant value) { | 598 StringBuffer writeJsCode(StringBuffer buffer, Constant value) { |
477 value.writeJsCode(buffer, this); | 599 value.writeJsCode(buffer, this); |
478 return buffer; | 600 return buffer; |
479 } | 601 } |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
579 Constant visitLiteralDouble(LiteralDouble node) { | 701 Constant visitLiteralDouble(LiteralDouble node) { |
580 return new DoubleConstant(node.value); | 702 return new DoubleConstant(node.value); |
581 } | 703 } |
582 | 704 |
583 Constant visitLiteralInt(LiteralInt node) { | 705 Constant visitLiteralInt(LiteralInt node) { |
584 return new IntConstant(node.value); | 706 return new IntConstant(node.value); |
585 } | 707 } |
586 | 708 |
587 Constant visitLiteralList(LiteralList node) { | 709 Constant visitLiteralList(LiteralList node) { |
588 if (!node.isConst()) error(node); | 710 if (!node.isConst()) error(node); |
589 List arguments = []; | 711 List<Constant> arguments = <Constant>[]; |
590 for (Link<Node> link = node.elements.nodes; | 712 for (Link<Node> link = node.elements.nodes; |
591 !link.isEmpty(); | 713 !link.isEmpty(); |
592 link = link.tail) { | 714 link = link.tail) { |
593 arguments.add(evaluate(link.head)); | 715 arguments.add(evaluate(link.head)); |
594 } | 716 } |
595 // TODO(floitsch): get type from somewhere. | 717 // TODO(floitsch): get type from somewhere. |
596 Type type = null; | 718 Type type = null; |
597 return constantHandler.compileListLiteral(node, type, arguments); | 719 return constantHandler.compileListLiteral(node, type, arguments); |
598 } | 720 } |
599 | 721 |
600 Constant visitLiteralMap(LiteralMap node) { | 722 Constant visitLiteralMap(LiteralMap node) { |
601 compiler.unimplemented("CompileTimeConstantEvaluator map", node: node); | 723 // TODO(floitsch): check for isConst, once the parser adds it into the node. |
| 724 // if (!node.isConst()) error(node); |
| 725 List<StringConstant> keys = <StringConstant>[]; |
| 726 List<Constant> values = <Constant>[]; |
| 727 bool hasProtoKey = false; |
| 728 for (Link<Node> link = node.entries.nodes; |
| 729 !link.isEmpty(); |
| 730 link = link.tail) { |
| 731 LiteralMapEntry entry = link.head; |
| 732 Constant key = evaluate(entry.key); |
| 733 if (!key.isString() || entry.key.asLiteralString() === null) { |
| 734 MessageKind kind = MessageKind.KEY_NOT_A_STRING_LITERAL; |
| 735 compiler.reportError(entry.key, new ResolutionError(kind, const [])); |
| 736 } |
| 737 // TODO(floitsch): make this faster. |
| 738 StringConstant keyConstant = key; |
| 739 if (keyConstant.value == new LiteralDartString("__proto__")) { |
| 740 hasProtoKey = true; |
| 741 } |
| 742 keys.add(key); |
| 743 values.add(evaluate(entry.value)); |
| 744 } |
| 745 if (hasProtoKey) { |
| 746 compiler.unimplemented("visitLiteralMap with __proto__ key", |
| 747 node: node); |
| 748 } |
| 749 // TODO(floitsch): this should be a List<String> type. |
| 750 Type keysType = null; |
| 751 ListConstant keysList = new ListConstant(keysType, keys); |
| 752 constantHandler.registerCompileTimeConstant(keysList); |
| 753 ClassElement classElement = |
| 754 compiler.jsHelperLibrary.find(MapConstant.DART_CLASS); |
| 755 classElement.ensureResolved(compiler); |
| 756 // TODO(floitsch): copy over the generic type. |
| 757 Type type = new SimpleType(classElement.name, classElement); |
| 758 compiler.registerInstantiatedClass(classElement); |
| 759 Constant constant = new MapConstant(type, keysList, values); |
| 760 constantHandler.registerCompileTimeConstant(constant); |
| 761 return constant; |
602 } | 762 } |
603 | 763 |
604 Constant visitLiteralNull(LiteralNull node) { | 764 Constant visitLiteralNull(LiteralNull node) { |
605 return new NullConstant(); | 765 return new NullConstant(); |
606 } | 766 } |
607 | 767 |
608 Constant visitLiteralString(LiteralString node) { | 768 Constant visitLiteralString(LiteralString node) { |
609 return new StringConstant(node.dartString); | 769 return new StringConstant(node.dartString); |
610 } | 770 } |
611 | 771 |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
756 arguments); | 916 arguments); |
757 } | 917 } |
758 | 918 |
759 error(Node node) { | 919 error(Node node) { |
760 // TODO(floitsch): get the list of constants that are currently compiled | 920 // TODO(floitsch): get the list of constants that are currently compiled |
761 // and present some kind of stack-trace. | 921 // and present some kind of stack-trace. |
762 MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT; | 922 MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT; |
763 compiler.reportError(node, new CompileTimeConstantError(kind, const [])); | 923 compiler.reportError(node, new CompileTimeConstantError(kind, const [])); |
764 } | 924 } |
765 } | 925 } |
OLD | NEW |