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 | |
24 /** | |
25 * Recursively adds the constants dependencies unless the dependency is | |
26 * already in the [seenConstants] list. Does not add itself to the [list]. | |
ngeoffray
2012/03/10 18:33:55
[seenConstants] list -> [seenConstants] set.
floitsch
2012/03/10 19:58:54
has been removed.
| |
27 * | |
28 * The dependent constants must always be added before the user of the | |
29 * constant. | |
30 * | |
31 * Any constant that is added to the list must also be added to the set. | |
32 */ | |
33 // TODO(floitsch): this should take an ordered set. | |
34 abstract void addDependencies(List<Constant> list, | |
ngeoffray
2012/03/10 18:33:55
Maybe rename list to orderedList. 'list' is too ge
floitsch
2012/03/10 19:58:54
has been removed.
| |
35 Set<Constant> seenConstants); | |
23 } | 36 } |
24 | 37 |
25 class PrimitiveConstant extends Constant { | 38 class PrimitiveConstant extends Constant { |
26 abstract get value(); | 39 abstract get value(); |
27 const PrimitiveConstant(); | 40 const PrimitiveConstant(); |
28 | 41 |
29 bool operator ==(var other) { | 42 bool operator ==(var other) { |
30 if (other is !PrimitiveConstant) return false; | 43 if (other is !PrimitiveConstant) return false; |
31 PrimitiveConstant otherPrimitive = other; | 44 PrimitiveConstant otherPrimitive = other; |
32 // We use == instead of === so that DartStrings compare correctly. | 45 // We use == instead of === so that DartStrings compare correctly. |
33 return value == otherPrimitive.value; | 46 return value == otherPrimitive.value; |
34 } | 47 } |
35 | 48 |
36 String toString() => value.toString(); | 49 String toString() => value.toString(); |
50 void addDependencies(List<Constant> list, Set<Constant> seenConstants) { | |
51 // Primitive constants don't have dependencies. | |
52 } | |
37 abstract DartString toDartString(); | 53 abstract DartString toDartString(); |
38 } | 54 } |
39 | 55 |
40 class NullConstant extends PrimitiveConstant { | 56 class NullConstant extends PrimitiveConstant { |
41 factory NullConstant() => const NullConstant._internal(); | 57 factory NullConstant() => const NullConstant._internal(); |
42 const NullConstant._internal(); | 58 const NullConstant._internal(); |
43 bool isNull() => true; | 59 bool isNull() => true; |
44 get value() => null; | 60 get value() => null; |
45 | 61 |
46 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | 62 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 | 247 |
232 int hashCode() => _hashCode; | 248 int hashCode() => _hashCode; |
233 DartString toDartString() => value; | 249 DartString toDartString() => value; |
234 } | 250 } |
235 | 251 |
236 class ObjectConstant extends Constant { | 252 class ObjectConstant extends Constant { |
237 final Type type; | 253 final Type type; |
238 | 254 |
239 ObjectConstant(this.type); | 255 ObjectConstant(this.type); |
240 bool isObject() => true; | 256 bool isObject() => true; |
257 | |
258 void addDependency(Constant constant, | |
ngeoffray
2012/03/10 18:33:55
Instead of putting addDependeny on a Constant clas
floitsch
2012/03/10 19:58:54
replaced with getDependencies().
| |
259 List<Constant> list, | |
260 Set<Constant> seenConstants) { | |
261 if (!seenConstants.contains(constant)) { | |
262 constant.addDependencies(list, seenConstants); | |
263 list.add(constant); | |
264 seenConstants.add(constant); | |
265 } | |
266 } | |
241 } | 267 } |
242 | 268 |
243 class ListConstant extends ObjectConstant { | 269 class ListConstant extends ObjectConstant { |
244 final List<Constant> entries; | 270 final List<Constant> entries; |
245 int _hashCode; | 271 int _hashCode; |
246 | 272 |
247 ListConstant(Type type, this.entries) : super(type) { | 273 ListConstant(Type type, this.entries) : super(type) { |
248 // TODO(floitsch): create a better hash. | 274 // TODO(floitsch): create a better hash. |
249 int hash = 0; | 275 int hash = 0; |
250 for (Constant input in entries) hash ^= input.hashCode(); | 276 for (Constant input in entries) hash ^= input.hashCode(); |
(...skipping 17 matching lines...) Expand all Loading... | |
268 entry.writeJsCode(buffer, handler); | 294 entry.writeJsCode(buffer, handler); |
269 } | 295 } |
270 } | 296 } |
271 buffer.add("])"); | 297 buffer.add("])"); |
272 } | 298 } |
273 | 299 |
274 bool operator ==(var other) { | 300 bool operator ==(var other) { |
275 if (other is !ListConstant) return false; | 301 if (other is !ListConstant) return false; |
276 ListConstant otherList = other; | 302 ListConstant otherList = other; |
277 if (hashCode() != otherList.hashCode()) return false; | 303 if (hashCode() != otherList.hashCode()) return false; |
278 // TODO(floitsch): verify that the types are the same. | 304 // TODO(floitsch): verify that the generic types are the same. |
279 if (entries.length != otherList.entries.length) return false; | 305 if (entries.length != otherList.entries.length) return false; |
280 for (int i = 0; i < entries.length; i++) { | 306 for (int i = 0; i < entries.length; i++) { |
281 if (entries[i] != otherList.entries[i]) return false; | 307 if (entries[i] != otherList.entries[i]) return false; |
282 } | 308 } |
283 return true; | 309 return true; |
284 } | 310 } |
285 | 311 |
286 int hashCode() => _hashCode; | 312 int hashCode() => _hashCode; |
313 | |
314 void addDependencies(List<Constant> list, Set<Constant> seenConstants) { | |
315 for (Constant constant in entries) { | |
316 addDependency(constant, list, seenConstants); | |
317 } | |
318 } | |
319 } | |
320 | |
321 class MapConstant extends ObjectConstant { | |
322 /** The dart class implementing constant map literals. */ | |
323 static final SourceString DART_CLASS = const SourceString("ConstantMap"); | |
324 static final SourceString LENGTH_NAME = const SourceString("length"); | |
325 static final SourceString JS_OBJECT_NAME = const SourceString("_jsObject"); | |
326 static final SourceString KEYS_NAME = const SourceString("_keys"); | |
327 | |
328 final ListConstant keys; | |
329 final List<Constant> values; | |
330 int _hashCode; | |
331 | |
332 MapConstant(Type type, this.keys, this.values) : super(type) { | |
333 // TODO(floitsch): create a better hash. | |
334 int hash = 0; | |
335 for (Constant value in values) hash ^= value.hashCode(); | |
336 _hashCode = hash; | |
337 } | |
338 bool isMap() => true; | |
339 | |
340 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | |
341 String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype"; | |
342 | |
343 void writeJsMap() { | |
344 buffer.add("{"); | |
345 for (int i = 0; i < keys.entries.length; i++) { | |
346 if (i != 0) buffer.add(", "); | |
347 | |
348 StringConstant key = keys.entries[i]; | |
349 key.writeJsCode(buffer, handler); | |
350 buffer.add(": "); | |
351 Constant value = values[i]; | |
352 // TODO(floitsch): share this code with the ListConstant and | |
353 // ConstructedConstant. | |
354 if (value.isObject()) { | |
355 String name = handler.getNameForConstant(value); | |
356 buffer.add("$isolatePrototype.$name"); | |
357 } else { | |
358 value.writeJsCode(buffer, handler); | |
359 } | |
360 } | |
361 buffer.add("}"); | |
362 } | |
363 | |
364 void badFieldCountError() { | |
365 handler.compiler.internalError( | |
366 "Compiler and ConstantMap disagree on number of fields."); | |
367 } | |
368 | |
369 ClassElement classElement = type.element; | |
370 buffer.add("new "); | |
371 buffer.add(handler.getJsConstructor(classElement)); | |
372 buffer.add("("); | |
373 // The arguments of the JavaScript constructor for any given Dart class | |
374 // are in the same order as the members of the class element. | |
375 int emittedArgumentCount = 0; | |
376 for (Element element in classElement.members) { | |
377 if (element.name == LENGTH_NAME) { | |
378 buffer.add(keys.entries.length); | |
379 } else if (element.name == JS_OBJECT_NAME) { | |
380 writeJsMap(); | |
381 } else if (element.name == KEYS_NAME) { | |
382 // TODO(floitsch): share this code with the ListConstant and | |
383 // ConstructedConstant. | |
384 String name = handler.getNameForConstant(keys); | |
385 buffer.add("$isolatePrototype.$name"); | |
386 } else { | |
387 // Skip methods. | |
388 if (element.kind == ElementKind.FIELD) badFieldCountError(); | |
389 continue; | |
390 } | |
391 emittedArgumentCount++; | |
392 if (emittedArgumentCount == 3) { | |
393 break; // All arguments have been emitted. | |
394 } else { | |
395 buffer.add(", "); | |
396 } | |
397 } | |
398 if (emittedArgumentCount != 3) badFieldCountError(); | |
399 buffer.add(")"); | |
400 } | |
401 | |
402 bool operator ==(var other) { | |
403 if (other is !MapConstant) return false; | |
404 MapConstant otherMap = other; | |
405 if (hashCode() != otherMap.hashCode()) return false; | |
406 // TODO(floitsch): verify that the generic types are the same. | |
407 if (keys != otherMap.keys) return false; | |
408 for (int i = 0; i < values.length; i++) { | |
409 if (values[i] != otherMap.values[i]) return false; | |
410 } | |
411 return true; | |
412 } | |
413 | |
414 int hashCode() => _hashCode; | |
415 | |
416 void addDependencies(List<Constant> list, Set<Constant> seenConstants) { | |
417 addDependency(keys, list, seenConstants); | |
418 for (Constant constant in values) { | |
419 addDependency(constant, list, seenConstants); | |
420 } | |
421 } | |
287 } | 422 } |
288 | 423 |
289 class ConstructedConstant extends ObjectConstant { | 424 class ConstructedConstant extends ObjectConstant { |
290 final List<Constant> fields; | 425 final List<Constant> fields; |
291 int _hashCode; | 426 int _hashCode; |
292 | 427 |
293 ConstructedConstant(Type type, this.fields) : super(type) { | 428 ConstructedConstant(Type type, this.fields) : super(type) { |
294 assert(type !== null); | 429 assert(type !== null); |
295 // TODO(floitsch): create a better hash. | 430 // TODO(floitsch): create a better hash. |
296 int hash = 0; | 431 int hash = 0; |
297 for (Constant field in fields) { | 432 for (Constant field in fields) { |
298 hash ^= field.hashCode(); | 433 hash ^= field.hashCode(); |
299 } | 434 } |
300 hash ^= type.element.hashCode(); | 435 hash ^= type.element.hashCode(); |
301 _hashCode = hash; | 436 _hashCode = hash; |
302 } | 437 } |
303 bool isConstructedObject() => true; | 438 bool isConstructedObject() => true; |
304 | 439 |
305 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { | 440 void writeJsCode(StringBuffer buffer, ConstantHandler handler) { |
306 buffer.add("new "); | 441 buffer.add("new "); |
307 buffer.add(handler.getJsConstructor(type.element)); | 442 buffer.add(handler.getJsConstructor(type.element)); |
308 buffer.add("("); | 443 buffer.add("("); |
309 String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype"; | 444 String isolatePrototype = "${handler.compiler.namer.ISOLATE}.prototype"; |
310 for (int i = 0; i < fields.length; i++) { | 445 for (int i = 0; i < fields.length; i++) { |
311 if (i != 0) buffer.add(", "); | 446 if (i != 0) buffer.add(", "); |
312 Constant field = fields[i]; | 447 Constant field = fields[i]; |
313 // TODO(floitsch): share this code with the ListConstant. | 448 // TODO(floitsch): share this code with the ListConstant. |
314 if (field.isObject()) { | 449 if (field.isObject()) { |
315 String name = handler.getNameForConstant(entry); | 450 String name = handler.getNameForConstant(field); |
316 buffer.add("$isolatePrototype.$name"); | 451 buffer.add("$isolatePrototype.$name"); |
317 } else { | 452 } else { |
318 field.writeJsCode(buffer, handler); | 453 field.writeJsCode(buffer, handler); |
319 } | 454 } |
320 } | 455 } |
321 buffer.add(")"); | 456 buffer.add(")"); |
322 } | 457 } |
323 | 458 |
324 bool operator ==(var otherVar) { | 459 bool operator ==(var otherVar) { |
325 if (otherVar is !ConstructedConstant) return false; | 460 if (otherVar is !ConstructedConstant) return false; |
326 ConstructedConstant other = otherVar; | 461 ConstructedConstant other = otherVar; |
327 if (hashCode() != other.hashCode()) return false; | 462 if (hashCode() != other.hashCode()) return false; |
328 // TODO(floitsch): verify that the (generic) types are the same. | 463 // TODO(floitsch): verify that the (generic) types are the same. |
329 if (type.element != other.type.element) return false; | 464 if (type.element != other.type.element) return false; |
330 if (fields.length != other.fields.length) return false; | 465 if (fields.length != other.fields.length) return false; |
331 for (int i = 0; i < fields.length; i++) { | 466 for (int i = 0; i < fields.length; i++) { |
332 if (fields[i] != other.fields[i]) return false; | 467 if (fields[i] != other.fields[i]) return false; |
333 } | 468 } |
334 return true; | 469 return true; |
335 } | 470 } |
336 | 471 |
337 int hashCode() => _hashCode; | 472 int hashCode() => _hashCode; |
473 | |
474 void addDependencies(List<Constant> list, Set<Constant> seenConstants) { | |
475 for (Constant constant in fields) { | |
476 addDependency(constant, list, seenConstants); | |
477 } | |
478 } | |
338 } | 479 } |
339 | 480 |
340 /** | 481 /** |
341 * The [ConstantHandler] keeps track of compile-time constants, | 482 * The [ConstantHandler] keeps track of compile-time constants, |
342 * initializations of global and static fields, and default values of | 483 * initializations of global and static fields, and default values of |
343 * optional parameters. | 484 * optional parameters. |
344 */ | 485 */ |
345 class ConstantHandler extends CompilerTask { | 486 class ConstantHandler extends CompilerTask { |
346 // Contains the initial value of fields. Must contain all static and global | 487 // Contains the initial value of fields. Must contain all static and global |
347 // initializations of used fields. May contain caches for instance fields. | 488 // initializations of used fields. May contain caches for instance fields. |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
459 */ | 600 */ |
460 List<VariableElement> getStaticFinalFieldsForEmission() { | 601 List<VariableElement> getStaticFinalFieldsForEmission() { |
461 return initialVariableValues.getKeys().filter((element) { | 602 return initialVariableValues.getKeys().filter((element) { |
462 return element.kind == ElementKind.FIELD | 603 return element.kind == ElementKind.FIELD |
463 && !element.isInstanceMember() | 604 && !element.isInstanceMember() |
464 && element.modifiers.isFinal(); | 605 && element.modifiers.isFinal(); |
465 }); | 606 }); |
466 } | 607 } |
467 | 608 |
468 List<Constant> getConstantsForEmission() { | 609 List<Constant> getConstantsForEmission() { |
469 return compiledConstants.getKeys(); | 610 // TODO(floitsch): Improve the creation of the dependencies. |
611 Set<Constant> seenConstants = new Set<Constant>(); | |
612 List<Constant> result = new List<Constant>(); | |
613 compiledConstants.forEach((Constant key, ignored) { | |
614 if (seenConstants.contains(key)) return; | |
615 key.addDependencies(result, seenConstants); | |
616 assert(!seenConstants.contains(key)); | |
617 seenConstants.add(key); | |
618 result.add(key); | |
619 }); | |
620 return result; | |
470 } | 621 } |
471 | 622 |
472 String getNameForConstant(Constant constant) { | 623 String getNameForConstant(Constant constant) { |
473 return compiledConstants[constant]; | 624 return compiledConstants[constant]; |
474 } | 625 } |
475 | 626 |
476 StringBuffer writeJsCode(StringBuffer buffer, Constant value) { | 627 StringBuffer writeJsCode(StringBuffer buffer, Constant value) { |
477 value.writeJsCode(buffer, this); | 628 value.writeJsCode(buffer, this); |
478 return buffer; | 629 return buffer; |
479 } | 630 } |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
585 Constant visitLiteralDouble(LiteralDouble node) { | 736 Constant visitLiteralDouble(LiteralDouble node) { |
586 return new DoubleConstant(node.value); | 737 return new DoubleConstant(node.value); |
587 } | 738 } |
588 | 739 |
589 Constant visitLiteralInt(LiteralInt node) { | 740 Constant visitLiteralInt(LiteralInt node) { |
590 return new IntConstant(node.value); | 741 return new IntConstant(node.value); |
591 } | 742 } |
592 | 743 |
593 Constant visitLiteralList(LiteralList node) { | 744 Constant visitLiteralList(LiteralList node) { |
594 if (!node.isConst()) error(node); | 745 if (!node.isConst()) error(node); |
595 List arguments = []; | 746 List<Constant> arguments = <Constant>[]; |
596 for (Link<Node> link = node.elements.nodes; | 747 for (Link<Node> link = node.elements.nodes; |
597 !link.isEmpty(); | 748 !link.isEmpty(); |
598 link = link.tail) { | 749 link = link.tail) { |
599 arguments.add(evaluate(link.head)); | 750 arguments.add(evaluate(link.head)); |
600 } | 751 } |
601 // TODO(floitsch): get type from somewhere. | 752 // TODO(floitsch): get type from somewhere. |
602 Type type = null; | 753 Type type = null; |
603 return constantHandler.compileListLiteral(node, type, arguments); | 754 return constantHandler.compileListLiteral(node, type, arguments); |
604 } | 755 } |
605 | 756 |
606 Constant visitLiteralMap(LiteralMap node) { | 757 Constant visitLiteralMap(LiteralMap node) { |
607 compiler.unimplemented("CompileTimeConstantEvaluator map", node: node); | 758 // TODO(floitsch): check for isConst, once the parser adds it into the node. |
759 // if (!node.isConst()) error(node); | |
760 List<StringConstant> keys = <StringConstant>[]; | |
761 List<Constant> values = <Constant>[]; | |
762 bool hasProtoKey = false; | |
763 for (Link<Node> link = node.entries.nodes; | |
764 !link.isEmpty(); | |
765 link = link.tail) { | |
766 LiteralMapEntry entry = link.head; | |
767 Constant key = evaluate(entry.key); | |
768 if (!key.isString() || | |
769 (entry.key.asLiteralString() === null && | |
770 entry.key.asStringInterpolation() === null)) { | |
ngeoffray
2012/03/10 18:33:55
AFAIK, string interpolation for constants is still
floitsch
2012/03/10 19:58:54
This is in preparation for future changes.
Since s
ngeoffray
2012/03/10 22:28:03
I'd prefer that you remove it. I have a strong ave
floitsch
2012/03/10 22:58:22
Done.
| |
771 MessageKind kind = MessageKind.KEY_NOT_A_STRING_LITERAL; | |
772 // TODO(floitsch): is resolution error the correct error? | |
773 compiler.reportError(entry.key, new ResolutionError(kind, const [])); | |
774 } | |
775 // TODO(floitsch): make this faster. | |
776 StringConstant keyConstant = key; | |
777 if (keyConstant.value == new LiteralDartString("__proto__")) { | |
778 hasProtoKey = true; | |
779 } | |
780 keys.add(key); | |
781 values.add(evaluate(entry.value)); | |
782 } | |
783 if (hasProtoKey) { | |
784 compiler.unimplemented("visitLiteralMap with __proto__ key", | |
785 node: node); | |
786 } | |
787 // TODO(floitsch): this should be a List<String> type. | |
788 Type keysType = null; | |
789 ListConstant keysList = new ListConstant(keysType, keys); | |
790 constantHandler.registerCompileTimeConstant(keysList); | |
791 ClassElement classElement = | |
792 compiler.jsHelperLibrary.find(MapConstant.DART_CLASS); | |
793 classElement.ensureResolved(compiler); | |
794 // TODO(floitsch): copy over the generic type. | |
795 Type type = new SimpleType(classElement.name, classElement); | |
796 compiler.registerInstantiatedClass(classElement); | |
797 Constant constant = new MapConstant(type, keysList, values); | |
798 constantHandler.registerCompileTimeConstant(constant); | |
799 return constant; | |
608 } | 800 } |
609 | 801 |
610 Constant visitLiteralNull(LiteralNull node) { | 802 Constant visitLiteralNull(LiteralNull node) { |
611 return new NullConstant(); | 803 return new NullConstant(); |
612 } | 804 } |
613 | 805 |
614 Constant visitLiteralString(LiteralString node) { | 806 Constant visitLiteralString(LiteralString node) { |
615 return new StringConstant(node.dartString); | 807 return new StringConstant(node.dartString); |
616 } | 808 } |
617 | 809 |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
762 arguments); | 954 arguments); |
763 } | 955 } |
764 | 956 |
765 error(Node node) { | 957 error(Node node) { |
766 // TODO(floitsch): get the list of constants that are currently compiled | 958 // TODO(floitsch): get the list of constants that are currently compiled |
767 // and present some kind of stack-trace. | 959 // and present some kind of stack-trace. |
768 MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT; | 960 MessageKind kind = MessageKind.NOT_A_COMPILE_TIME_CONSTANT; |
769 compiler.reportError(node, new CompileTimeConstantError(kind, const [])); | 961 compiler.reportError(node, new CompileTimeConstantError(kind, const [])); |
770 } | 962 } |
771 } | 963 } |
OLD | NEW |