Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1058)

Side by Side Diff: frog/leg/compile_time_constants.dart

Issue 9873021: Move frog/leg to lib/compiler/implementation. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « frog/leg/colors.dart ('k') | frog/leg/compiler.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « frog/leg/colors.dart ('k') | frog/leg/compiler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698