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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/compile_time_constants.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
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 part of dart2js;
6
7 /// A [ConstantEnvironment] provides access for constants compiled for variable
8 /// initializers.
9 abstract class ConstantEnvironment {
10 /// Returns the constant for the initializer of [element].
11 ConstantExpression getConstantForVariable(VariableElement element);
12 }
13
14 /// A class that can compile and provide constants for variables, nodes and
15 /// metadata.
16 abstract class ConstantCompiler extends ConstantEnvironment {
17 /// Compiles the compile-time constant for the initializer of [element], or
18 /// reports an error if the initializer is not a compile-time constant.
19 ///
20 /// Depending on implementation, the constant compiler might also compute
21 /// the compile-time constant for the backend interpretation of constants.
22 ///
23 /// The returned constant is always of the frontend interpretation.
24 ConstantExpression compileConstant(VariableElement element);
25
26 /// Computes the compile-time constant for the variable initializer,
27 /// if possible.
28 void compileVariable(VariableElement element);
29
30 /// Compiles the compile-time constant for [node], or reports an error if
31 /// [node] is not a compile-time constant.
32 ///
33 /// Depending on implementation, the constant compiler might also compute
34 /// the compile-time constant for the backend interpretation of constants.
35 ///
36 /// The returned constant is always of the frontend interpretation.
37 ConstantExpression compileNode(Node node, TreeElements elements);
38
39 /// Compiles the compile-time constant for the value [metadata], or reports an
40 /// error if the value is not a compile-time constant.
41 ///
42 /// Depending on implementation, the constant compiler might also compute
43 /// the compile-time constant for the backend interpretation of constants.
44 ///
45 /// The returned constant is always of the frontend interpretation.
46 ConstantExpression compileMetadata(MetadataAnnotation metadata,
47 Node node,
48 TreeElements elements);
49 }
50
51 /// A [BackendConstantEnvironment] provides access to constants needed for
52 /// backend implementation.
53 abstract class BackendConstantEnvironment extends ConstantEnvironment {
54 /// Returns the compile-time constant associated with [node].
55 ///
56 /// Depending on implementation, the constant might be stored in [elements].
57 ConstantExpression getConstantForNode(Node node, TreeElements elements);
58
59 /// Returns the compile-time constant value of [metadata].
60 ConstantExpression getConstantForMetadata(MetadataAnnotation metadata);
61 }
62
63 /// Interface for the task that compiles the constant environments for the
64 /// frontend and backend interpretation of compile-time constants.
65 abstract class ConstantCompilerTask extends CompilerTask
66 implements ConstantCompiler {
67 ConstantCompilerTask(Compiler compiler) : super(compiler);
68 }
69
70 /**
71 * The [ConstantCompilerBase] is provides base implementation for compilation of
72 * compile-time constants for both the Dart and JavaScript interpretation of
73 * constants. It keeps track of compile-time constants for initializations of
74 * global and static fields, and default values of optional parameters.
75 */
76 abstract class ConstantCompilerBase implements ConstantCompiler {
77 final Compiler compiler;
78 final ConstantSystem constantSystem;
79
80 /**
81 * Contains the initial values of fields and default values of parameters.
82 *
83 * Must contain all static and global initializations of const fields.
84 *
85 * May contain eagerly compiled initial values for statics and instance
86 * fields (if those are compile-time constants).
87 *
88 * May contain default parameter values of optional arguments.
89 *
90 * Invariant: The keys in this map are declarations.
91 */
92 final Map<VariableElement, ConstantExpression> initialVariableValues =
93 new Map<VariableElement, ConstantExpression>();
94
95 /** The set of variable elements that are in the process of being computed. */
96 final Set<VariableElement> pendingVariables = new Set<VariableElement>();
97
98 ConstantCompilerBase(this.compiler, this.constantSystem);
99
100 ConstantExpression getConstantForVariable(VariableElement element) {
101 return initialVariableValues[element.declaration];
102 }
103
104 ConstantExpression compileConstant(VariableElement element) {
105 return compileVariable(element, isConst: true);
106 }
107
108 ConstantExpression compileVariable(VariableElement element,
109 {bool isConst: false}) {
110
111 if (initialVariableValues.containsKey(element.declaration)) {
112 ConstantExpression result = initialVariableValues[element.declaration];
113 return result;
114 }
115 AstElement currentElement = element.analyzableElement;
116 return compiler.withCurrentElement(currentElement, () {
117 compiler.analyzeElement(currentElement.declaration);
118 ConstantExpression constant = compileVariableWithDefinitions(
119 element, currentElement.resolvedAst.elements, isConst: isConst);
120 return constant;
121 });
122 }
123
124 /**
125 * Returns the a compile-time constant if the variable could be compiled
126 * eagerly. If the variable needs to be initialized lazily returns `null`.
127 * If the variable is `const` but cannot be compiled eagerly reports an
128 * error.
129 */
130 ConstantExpression compileVariableWithDefinitions(VariableElement element,
131 TreeElements definitions,
132 {bool isConst: false}) {
133 Node node = element.node;
134 if (pendingVariables.contains(element)) {
135 if (isConst) {
136 compiler.reportFatalError(
137 node, MessageKind.CYCLIC_COMPILE_TIME_CONSTANTS);
138 }
139 return null;
140 }
141 pendingVariables.add(element);
142
143 Expression initializer = element.initializer;
144 ConstantExpression value;
145 if (initializer == null) {
146 // No initial value.
147 value = new PrimitiveConstantExpression(new NullConstantValue());
148 } else {
149 value = compileNodeWithDefinitions(
150 initializer, definitions, isConst: isConst);
151 if (compiler.enableTypeAssertions &&
152 value != null &&
153 element.isField) {
154 DartType elementType = element.type;
155 if (elementType.isMalformed && !value.value.isNull) {
156 if (isConst) {
157 ErroneousElement element = elementType.element;
158 compiler.reportFatalError(
159 node, element.messageKind, element.messageArguments);
160 } else {
161 // We need to throw an exception at runtime.
162 value = null;
163 }
164 } else {
165 DartType constantType = value.value.computeType(compiler);
166 if (!constantSystem.isSubtype(compiler,
167 constantType, elementType)) {
168 if (isConst) {
169 compiler.reportFatalError(
170 node, MessageKind.NOT_ASSIGNABLE,
171 {'fromType': constantType, 'toType': elementType});
172 } else {
173 // If the field cannot be lazily initialized, we will throw
174 // the exception at runtime.
175 value = null;
176 }
177 }
178 }
179 }
180 }
181 if (value != null) {
182 initialVariableValues[element.declaration] = value;
183 } else {
184 assert(invariant(element, !isConst,
185 message: "Variable $element does not compile to a constant."));
186 }
187 pendingVariables.remove(element);
188 return value;
189 }
190
191 ConstantExpression compileNodeWithDefinitions(Node node,
192 TreeElements definitions,
193 {bool isConst: true}) {
194 assert(node != null);
195 CompileTimeConstantEvaluator evaluator = new CompileTimeConstantEvaluator(
196 this, definitions, compiler, isConst: isConst);
197 AstConstant constant = evaluator.evaluate(node);
198 return constant != null ? constant.expression : null;
199 }
200
201 ConstantExpression compileNode(Node node, TreeElements elements) {
202 return compileNodeWithDefinitions(node, elements);
203 }
204
205 ConstantExpression compileMetadata(MetadataAnnotation metadata,
206 Node node,
207 TreeElements elements) {
208 return compileNodeWithDefinitions(node, elements);
209 }
210
211 void forgetElement(Element element) {
212 initialVariableValues.remove(element);
213 if (element is ScopeContainerElement) {
214 element.forEachLocalMember(initialVariableValues.remove);
215 }
216 if (element is FunctionElement && element.hasFunctionSignature) {
217 element.functionSignature.forEachParameter(this.forgetElement);
218 }
219 }
220 }
221
222 /// [ConstantCompiler] that uses the Dart semantics for the compile-time
223 /// constant evaluation.
224 class DartConstantCompiler extends ConstantCompilerBase {
225 DartConstantCompiler(Compiler compiler)
226 : super(compiler, const DartConstantSystem());
227
228 ConstantExpression getConstantForNode(Node node, TreeElements definitions) {
229 return definitions.getConstant(node);
230 }
231
232 ConstantExpression getConstantForMetadata(MetadataAnnotation metadata) {
233 return metadata.constant;
234 }
235
236 ConstantExpression compileNodeWithDefinitions(Node node,
237 TreeElements definitions,
238 {bool isConst: true}) {
239 ConstantExpression constant = definitions.getConstant(node);
240 if (constant != null) {
241 return constant;
242 }
243 constant =
244 super.compileNodeWithDefinitions(node, definitions, isConst: isConst);
245 if (constant != null) {
246 definitions.setConstant(node, constant);
247 }
248 return constant;
249 }
250 }
251
252 // TODO(johnniwinther): Decouple the creation of [ConstExp] and [Constant] from
253 // front-end AST in order to reuse the evaluation for the shared front-end.
254 class CompileTimeConstantEvaluator extends Visitor<AstConstant> {
255 bool isEvaluatingConstant;
256 final ConstantCompilerBase handler;
257 final TreeElements elements;
258 final Compiler compiler;
259
260 Element get context => elements.analyzedElement;
261
262 CompileTimeConstantEvaluator(this.handler,
263 this.elements,
264 this.compiler,
265 {bool isConst: false})
266 : this.isEvaluatingConstant = isConst;
267
268 ConstantSystem get constantSystem => handler.constantSystem;
269
270 AstConstant evaluate(Node node) {
271 return node.accept(this);
272 }
273
274 AstConstant evaluateConstant(Node node) {
275 bool oldIsEvaluatingConstant = isEvaluatingConstant;
276 isEvaluatingConstant = true;
277 AstConstant result = node.accept(this);
278 isEvaluatingConstant = oldIsEvaluatingConstant;
279 assert(result != null);
280 return result;
281 }
282
283 AstConstant visitNode(Node node) {
284 return signalNotCompileTimeConstant(node);
285 }
286
287 AstConstant visitLiteralBool(LiteralBool node) {
288 return new AstConstant(
289 context, node, new PrimitiveConstantExpression(
290 constantSystem.createBool(node.value)));
291 }
292
293 AstConstant visitLiteralDouble(LiteralDouble node) {
294 return new AstConstant(
295 context, node, new PrimitiveConstantExpression(
296 constantSystem.createDouble(node.value)));
297 }
298
299 AstConstant visitLiteralInt(LiteralInt node) {
300 return new AstConstant(
301 context, node, new PrimitiveConstantExpression(
302 constantSystem.createInt(node.value)));
303 }
304
305 AstConstant visitLiteralList(LiteralList node) {
306 if (!node.isConst) {
307 return signalNotCompileTimeConstant(node);
308 }
309 List<ConstantExpression> argumentExpressions = <ConstantExpression>[];
310 List<ConstantValue> argumentValues = <ConstantValue>[];
311 for (Link<Node> link = node.elements.nodes;
312 !link.isEmpty;
313 link = link.tail) {
314 AstConstant argument = evaluateConstant(link.head);
315 if (argument == null) {
316 return null;
317 }
318 argumentExpressions.add(argument.expression);
319 argumentValues.add(argument.value);
320 }
321 DartType type = elements.getType(node);
322 return new AstConstant(
323 context, node, new ListConstantExpression(
324 new ListConstantValue(type, argumentValues),
325 type,
326 argumentExpressions));
327 }
328
329 AstConstant visitLiteralMap(LiteralMap node) {
330 if (!node.isConst) {
331 return signalNotCompileTimeConstant(node);
332 }
333 List<ConstantExpression> keyExpressions = <ConstantExpression>[];
334 List<ConstantValue> keyValues = <ConstantValue>[];
335 Map<ConstantValue, ConstantExpression> map =
336 new Map<ConstantValue, ConstantExpression>();
337 for (Link<Node> link = node.entries.nodes;
338 !link.isEmpty;
339 link = link.tail) {
340 LiteralMapEntry entry = link.head;
341 AstConstant key = evaluateConstant(entry.key);
342 if (key == null) {
343 return null;
344 }
345 if (!map.containsKey(key.value)) {
346 keyExpressions.add(key.expression);
347 keyValues.add(key.value);
348 } else {
349 compiler.reportWarning(entry.key, MessageKind.EQUAL_MAP_ENTRY_KEY);
350 }
351 AstConstant value = evaluateConstant(entry.value);
352 if (value == null) {
353 return null;
354 }
355 map[key.value] = value.expression;
356 }
357 List<ConstantExpression> valueExpressions = map.values.toList();
358 InterfaceType type = elements.getType(node);
359 return new AstConstant(
360 context, node, new MapConstantExpression(
361 constantSystem.createMap(compiler, type, keyValues,
362 valueExpressions.map((e) => e.value).toList()),
363 type,
364 keyExpressions,
365 valueExpressions));
366 }
367
368 AstConstant visitLiteralNull(LiteralNull node) {
369 return new AstConstant(
370 context, node, new PrimitiveConstantExpression(
371 constantSystem.createNull()));
372 }
373
374 AstConstant visitLiteralString(LiteralString node) {
375 return new AstConstant(
376 context, node, new PrimitiveConstantExpression(
377 constantSystem.createString(node.dartString)));
378 }
379
380 AstConstant visitStringJuxtaposition(StringJuxtaposition node) {
381 AstConstant left = evaluate(node.first);
382 AstConstant right = evaluate(node.second);
383 if (left == null || right == null) return null;
384 StringConstantValue leftValue = left.value;
385 StringConstantValue rightValue = right.value;
386 return new AstConstant(
387 context, node, new ConcatenateConstantExpression(
388 constantSystem.createString(
389 new DartString.concat(
390 leftValue.primitiveValue, rightValue.primitiveValue)),
391 [left.expression, right.expression]));
392 }
393
394 AstConstant visitStringInterpolation(StringInterpolation node) {
395 List<ConstantExpression> subexpressions = <ConstantExpression>[];
396 AstConstant initialString = evaluate(node.string);
397 if (initialString == null) {
398 return null;
399 }
400 subexpressions.add(initialString.expression);
401 StringConstantValue initialStringValue = initialString.value;
402 DartString accumulator = initialStringValue.primitiveValue;
403 for (StringInterpolationPart part in node.parts) {
404 AstConstant subexpression = evaluate(part.expression);
405 if (subexpression == null) {
406 return null;
407 }
408 subexpressions.add(subexpression.expression);
409 ConstantValue expression = subexpression.value;
410 DartString expressionString;
411 if (expression.isNum || expression.isBool) {
412 PrimitiveConstantValue primitive = expression;
413 expressionString =
414 new DartString.literal(primitive.primitiveValue.toString());
415 } else if (expression.isString) {
416 PrimitiveConstantValue primitive = expression;
417 expressionString = primitive.primitiveValue;
418 } else {
419 // TODO(johnniwinther): Specialize message to indicated that the problem
420 // is not constness but the types of the const expressions.
421 return signalNotCompileTimeConstant(part.expression);
422 }
423 accumulator = new DartString.concat(accumulator, expressionString);
424 AstConstant partString = evaluate(part.string);
425 if (partString == null) return null;
426 subexpressions.add(partString.expression);
427 StringConstantValue partStringValue = partString.value;
428 accumulator =
429 new DartString.concat(accumulator, partStringValue.primitiveValue);
430 };
431 return new AstConstant(
432 context, node, new ConcatenateConstantExpression(
433 constantSystem.createString(accumulator),
434 subexpressions));
435 }
436
437 AstConstant visitLiteralSymbol(LiteralSymbol node) {
438 InterfaceType type = compiler.symbolClass.rawType;
439 String text = node.slowNameString;
440 List<AstConstant> arguments =
441 <AstConstant>[new AstConstant(context, node,
442 new PrimitiveConstantExpression(constantSystem.createString(
443 new DartString.literal(text))))];
444 AstConstant constant = makeConstructedConstant(
445 compiler, handler, context, node, type, compiler.symbolConstructor,
446 new Selector.callConstructor('', null, 1),
447 arguments, arguments);
448 return new AstConstant(
449 context, node, new SymbolConstantExpression(constant.value, text));
450 }
451
452 AstConstant makeTypeConstant(Node node, DartType elementType) {
453 DartType constantType =
454 compiler.backend.typeImplementation.computeType(compiler);
455 return new AstConstant(
456 context, node, new TypeConstantExpression(
457 new TypeConstantValue(elementType, constantType),
458 elementType));
459 }
460
461 /// Returns true if the prefix of the send resolves to a deferred import
462 /// prefix.
463 bool isDeferredUse(Send send) {
464 if (send == null) return false;
465 return compiler.deferredLoadTask
466 .deferredPrefixElement(send, elements) != null;
467 }
468
469 AstConstant visitIdentifier(Identifier node) {
470 Element element = elements[node];
471 if (Elements.isClass(element) || Elements.isTypedef(element)) {
472 TypeDeclarationElement typeDeclarationElement = element;
473 DartType type = typeDeclarationElement.rawType;
474 return makeTypeConstant(node, type);
475 }
476 return signalNotCompileTimeConstant(node);
477 }
478
479 // TODO(floitsch): provide better error-messages.
480 AstConstant visitSend(Send send) {
481 Element element = elements[send];
482 if (send.isPropertyAccess) {
483 if (isDeferredUse(send)) {
484 return signalNotCompileTimeConstant(send,
485 message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT);
486 }
487 if (Elements.isStaticOrTopLevelFunction(element)) {
488 return new AstConstant(
489 context, send, new FunctionConstantExpression(
490 new FunctionConstantValue(element),
491 element));
492 } else if (Elements.isStaticOrTopLevelField(element)) {
493 ConstantExpression result;
494 if (element.isConst) {
495 result = handler.compileConstant(element);
496 } else if (element.isFinal && !isEvaluatingConstant) {
497 result = handler.compileVariable(element);
498 }
499 if (result != null) {
500 return new AstConstant(
501 context, send,
502 new VariableConstantExpression(result.value, element));
503 }
504 } else if (Elements.isClass(element) || Elements.isTypedef(element)) {
505 assert(elements.isTypeLiteral(send));
506 return makeTypeConstant(send, elements.getTypeLiteralType(send));
507 } else if (send.receiver != null) {
508 // Fall through to error handling.
509 } else if (!Elements.isUnresolved(element)
510 && element.isVariable
511 && element.isConst) {
512 ConstantExpression result = handler.compileConstant(element);
513 if (result != null) {
514 return new AstConstant(
515 context, send,
516 new VariableConstantExpression(result.value, element));
517 }
518 }
519 return signalNotCompileTimeConstant(send);
520 } else if (send.isCall) {
521 if (identical(element, compiler.identicalFunction)
522 && send.argumentCount() == 2) {
523 AstConstant left = evaluate(send.argumentsNode.nodes.head);
524 AstConstant right = evaluate(send.argumentsNode.nodes.tail.head);
525 if (left == null || right == null) {
526 return null;
527 }
528 ConstantValue result =
529 constantSystem.identity.fold(left.value, right.value);
530 if (result != null) {
531 return new AstConstant(
532 context, send, new BinaryConstantExpression(result,
533 left.expression, 'identical', right.expression));
534 }
535 }
536 return signalNotCompileTimeConstant(send);
537 } else if (send.isPrefix) {
538 assert(send.isOperator);
539 AstConstant receiverConstant = evaluate(send.receiver);
540 if (receiverConstant == null) {
541 return null;
542 }
543 Operator op = send.selector;
544 UnaryOperation operation = constantSystem.lookupUnary(op.source);
545 if (operation == null) {
546 compiler.internalError(op, "Unexpected operator.");
547 }
548 ConstantValue folded = operation.fold(receiverConstant.value);
549 if (folded == null) {
550 return signalNotCompileTimeConstant(send);
551 }
552 return new AstConstant(
553 context, send, new UnaryConstantExpression(folded,
554 op.source, receiverConstant.expression));
555 } else if (send.isOperator && !send.isPostfix) {
556 assert(send.argumentCount() == 1);
557 AstConstant left = evaluate(send.receiver);
558 AstConstant right = evaluate(send.argumentsNode.nodes.head);
559 if (left == null || right == null) {
560 return null;
561 }
562 ConstantValue leftValue = left.value;
563 ConstantValue rightValue = right.value;
564 Operator op = send.selector.asOperator();
565 ConstantValue folded = null;
566 switch (op.source) {
567 case "==":
568 if (leftValue.isPrimitive && rightValue.isPrimitive) {
569 folded = constantSystem.equal.fold(leftValue, rightValue);
570 }
571 break;
572 case "!=":
573 if (leftValue.isPrimitive && rightValue.isPrimitive) {
574 BoolConstantValue areEquals =
575 constantSystem.equal.fold(leftValue, rightValue);
576 if (areEquals == null) {
577 folded = null;
578 } else {
579 folded = areEquals.negate();
580 }
581 }
582 break;
583 default:
584 BinaryOperation operation = constantSystem.lookupBinary(op.source);
585 if (operation != null) {
586 folded = operation.fold(leftValue, rightValue);
587 }
588 }
589 if (folded == null) {
590 return signalNotCompileTimeConstant(send);
591 }
592 return new AstConstant(
593 context, send, new BinaryConstantExpression(folded,
594 left.expression, op.source, right.expression));
595 }
596 return signalNotCompileTimeConstant(send);
597 }
598
599 AstConstant visitConditional(Conditional node) {
600 AstConstant condition = evaluate(node.condition);
601 if (condition == null) {
602 return null;
603 } else if (!condition.value.isBool) {
604 DartType conditionType = condition.value.computeType(compiler);
605 if (isEvaluatingConstant) {
606 compiler.reportFatalError(
607 node.condition, MessageKind.NOT_ASSIGNABLE,
608 {'fromType': conditionType, 'toType': compiler.boolClass.rawType});
609 }
610 return null;
611 }
612 AstConstant thenExpression = evaluate(node.thenExpression);
613 AstConstant elseExpression = evaluate(node.elseExpression);
614 if (thenExpression == null || elseExpression == null) {
615 return null;
616 }
617 BoolConstantValue boolCondition = condition.value;
618 return new AstConstant(
619 context, node, new ConditionalConstantExpression(
620 boolCondition.primitiveValue
621 ? thenExpression.value
622 : elseExpression.value,
623 condition.expression,
624 thenExpression.expression,
625 elseExpression.expression));
626 }
627
628 AstConstant visitSendSet(SendSet node) {
629 return signalNotCompileTimeConstant(node);
630 }
631
632 /**
633 * Returns the normalized list of constant arguments that are passed to the
634 * constructor including both the concrete arguments and default values for
635 * omitted optional arguments.
636 *
637 * Invariant: [target] must be an implementation element.
638 */
639 List<AstConstant> evaluateArgumentsToConstructor(
640 Node node,
641 Selector selector,
642 Link<Node> arguments,
643 FunctionElement target,
644 {AstConstant compileArgument(Node node)}) {
645 assert(invariant(node, target.isImplementation));
646 List<AstConstant> compiledArguments = <AstConstant>[];
647
648 AstConstant compileDefaultValue(VariableElement element) {
649 ConstantExpression constant = handler.compileConstant(element);
650 return new AstConstant.fromDefaultValue(element, constant);
651 }
652 target.computeSignature(compiler);
653 bool succeeded = selector.addArgumentsToList(arguments,
654 compiledArguments,
655 target,
656 compileArgument,
657 compileDefaultValue,
658 compiler.world);
659 if (!succeeded) {
660 String name = Elements.constructorNameForDiagnostics(
661 target.enclosingClass.name, target.name);
662 compiler.reportFatalError(
663 node,
664 MessageKind.INVALID_CONSTRUCTOR_ARGUMENTS,
665 {'constructorName': name});
666 }
667 return compiledArguments;
668 }
669
670 AstConstant visitNewExpression(NewExpression node) {
671 if (!node.isConst) {
672 return signalNotCompileTimeConstant(node);
673 }
674
675 Send send = node.send;
676 FunctionElement constructor = elements[send];
677 if (Elements.isUnresolved(constructor)) {
678 return signalNotCompileTimeConstant(node);
679 }
680
681 // Deferred types can not be used in const instance creation expressions.
682 // Check if the constructor comes from a deferred library.
683 if (isDeferredUse(node.send.selector.asSend())) {
684 return signalNotCompileTimeConstant(node,
685 message: MessageKind.DEFERRED_COMPILE_TIME_CONSTANT_CONSTRUCTION);
686 }
687
688 // TODO(ahe): This is nasty: we must eagerly analyze the
689 // constructor to ensure the redirectionTarget has been computed
690 // correctly. Find a way to avoid this.
691 compiler.analyzeElement(constructor.declaration);
692
693 InterfaceType type = elements.getType(node);
694 Selector selector = elements.getSelector(send);
695
696 Map<Node, AstConstant> concreteArgumentMap =
697 <Node, AstConstant>{};
698 for (Link<Node> link = send.arguments; !link.isEmpty; link = link.tail) {
699 Node argument = link.head;
700 NamedArgument namedArgument = argument.asNamedArgument();
701 if (namedArgument != null) {
702 argument = namedArgument.expression;
703 }
704 concreteArgumentMap[argument] = evaluateConstant(argument);
705 }
706
707 List<AstConstant> normalizedArguments =
708 evaluateArgumentsToConstructor(
709 node, selector, send.arguments, constructor.implementation,
710 compileArgument: (node) => concreteArgumentMap[node]);
711 List<AstConstant> concreteArguments =
712 concreteArgumentMap.values.toList();
713
714 if (constructor == compiler.intEnvironment ||
715 constructor == compiler.boolEnvironment ||
716 constructor == compiler.stringEnvironment) {
717
718 AstConstant createEvaluatedConstant(ConstantValue value) {
719 return new AstConstant(
720 context, node, new ConstructedConstantExpresssion(
721 value,
722 type,
723 constructor,
724 elements.getSelector(send),
725 concreteArguments.map((e) => e.expression).toList()));
726 }
727
728 var firstArgument = normalizedArguments[0].value;
729 ConstantValue defaultValue = normalizedArguments[1].value;
730
731 if (firstArgument.isNull) {
732 compiler.reportFatalError(
733 send.arguments.head, MessageKind.NULL_NOT_ALLOWED);
734 return null;
735 }
736
737 if (!firstArgument.isString) {
738 DartType type = defaultValue.computeType(compiler);
739 compiler.reportFatalError(
740 send.arguments.head, MessageKind.NOT_ASSIGNABLE,
741 {'fromType': type, 'toType': compiler.stringClass.rawType});
742 return null;
743 }
744
745 if (constructor == compiler.intEnvironment &&
746 !(defaultValue.isNull || defaultValue.isInt)) {
747 DartType type = defaultValue.computeType(compiler);
748 compiler.reportFatalError(
749 send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE,
750 {'fromType': type, 'toType': compiler.intClass.rawType});
751 return null;
752 }
753
754 if (constructor == compiler.boolEnvironment &&
755 !(defaultValue.isNull || defaultValue.isBool)) {
756 DartType type = defaultValue.computeType(compiler);
757 compiler.reportFatalError(
758 send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE,
759 {'fromType': type, 'toType': compiler.boolClass.rawType});
760 return null;
761 }
762
763 if (constructor == compiler.stringEnvironment &&
764 !(defaultValue.isNull || defaultValue.isString)) {
765 DartType type = defaultValue.computeType(compiler);
766 compiler.reportFatalError(
767 send.arguments.tail.head, MessageKind.NOT_ASSIGNABLE,
768 {'fromType': type, 'toType': compiler.stringClass.rawType});
769 return null;
770 }
771
772 String value =
773 compiler.fromEnvironment(firstArgument.primitiveValue.slowToString());
774
775 if (value == null) {
776 return createEvaluatedConstant(defaultValue);
777 } else if (constructor == compiler.intEnvironment) {
778 int number = int.parse(value, onError: (_) => null);
779 return createEvaluatedConstant(
780 (number == null)
781 ? defaultValue
782 : constantSystem.createInt(number));
783 } else if (constructor == compiler.boolEnvironment) {
784 if (value == 'true') {
785 return createEvaluatedConstant(constantSystem.createBool(true));
786 } else if (value == 'false') {
787 return createEvaluatedConstant(constantSystem.createBool(false));
788 } else {
789 return createEvaluatedConstant(defaultValue);
790 }
791 } else {
792 assert(constructor == compiler.stringEnvironment);
793 return createEvaluatedConstant(
794 constantSystem.createString(new DartString.literal(value)));
795 }
796 } else {
797 return makeConstructedConstant(
798 compiler, handler, context,
799 node, type, constructor, selector,
800 concreteArguments, normalizedArguments);
801 }
802 }
803
804 static AstConstant makeConstructedConstant(
805 Compiler compiler,
806 ConstantCompilerBase handler,
807 Element context,
808 Node node,
809 InterfaceType type,
810 ConstructorElement constructor,
811 Selector selector,
812 List<AstConstant> concreteArguments,
813 List<AstConstant> normalizedArguments) {
814 assert(invariant(node, selector.applies(constructor, compiler.world),
815 message: "Selector $selector does not apply to constructor "
816 "$constructor."));
817
818 // The redirection chain of this element may not have been resolved through
819 // a post-process action, so we have to make sure it is done here.
820 compiler.resolver.resolveRedirectionChain(constructor, node);
821 InterfaceType constructedType =
822 constructor.computeEffectiveTargetType(type);
823 ConstructorElement target = constructor.effectiveTarget;
824 ClassElement classElement = target.enclosingClass;
825 // The constructor must be an implementation to ensure that field
826 // initializers are handled correctly.
827 target = target.implementation;
828 assert(invariant(node, target.isImplementation));
829
830 ConstructorEvaluator evaluator = new ConstructorEvaluator(
831 constructedType, target, handler, compiler);
832 evaluator.evaluateConstructorFieldValues(normalizedArguments);
833 List<AstConstant> fieldConstants =
834 evaluator.buildFieldConstants(classElement);
835
836 return new AstConstant(
837 context, node, new ConstructedConstantExpresssion(
838 new ConstructedConstantValue(
839 constructedType,
840 fieldConstants.map((e) => e.value).toList()),
841 type,
842 constructor,
843 selector,
844 concreteArguments.map((e) => e.expression).toList()));
845 }
846
847 AstConstant visitParenthesizedExpression(ParenthesizedExpression node) {
848 return node.expression.accept(this);
849 }
850
851 error(Node node, MessageKind message) {
852 // TODO(floitsch): get the list of constants that are currently compiled
853 // and present some kind of stack-trace.
854 compiler.reportFatalError(node, message);
855 }
856
857 AstConstant signalNotCompileTimeConstant(Node node,
858 {MessageKind message: MessageKind.NOT_A_COMPILE_TIME_CONSTANT}) {
859 if (isEvaluatingConstant) {
860 error(node, message);
861 }
862 // Else we don't need to do anything. The final handler is only
863 // optimistically trying to compile constants. So it is normal that we
864 // sometimes see non-compile time constants.
865 // Simply return [:null:] which is used to propagate a failing
866 // compile-time compilation.
867 return null;
868 }
869 }
870
871 class ConstructorEvaluator extends CompileTimeConstantEvaluator {
872 final InterfaceType constructedType;
873 final ConstructorElement constructor;
874 final Map<Element, AstConstant> definitions;
875 final Map<Element, AstConstant> fieldValues;
876
877 /**
878 * Documentation wanted -- johnniwinther
879 *
880 * Invariant: [constructor] must be an implementation element.
881 */
882 ConstructorEvaluator(InterfaceType this.constructedType,
883 FunctionElement constructor,
884 ConstantCompiler handler,
885 Compiler compiler)
886 : this.constructor = constructor,
887 this.definitions = new Map<Element, AstConstant>(),
888 this.fieldValues = new Map<Element, AstConstant>(),
889 super(handler,
890 compiler.resolver.resolveMethodElement(constructor.declaration),
891 compiler,
892 isConst: true) {
893 assert(invariant(constructor, constructor.isImplementation));
894 }
895
896 AstConstant visitSend(Send send) {
897 Element element = elements[send];
898 if (Elements.isLocal(element)) {
899 AstConstant constant = definitions[element];
900 if (constant == null) {
901 compiler.internalError(send, "Local variable without value.");
902 }
903 return constant;
904 }
905 return super.visitSend(send);
906 }
907
908 void potentiallyCheckType(Node node,
909 TypedElement element,
910 AstConstant constant) {
911 if (compiler.enableTypeAssertions) {
912 DartType elementType = element.type.substByContext(constructedType);
913 DartType constantType = constant.value.computeType(compiler);
914 if (!constantSystem.isSubtype(compiler, constantType, elementType)) {
915 compiler.withCurrentElement(constant.element, () {
916 compiler.reportFatalError(
917 constant.node, MessageKind.NOT_ASSIGNABLE,
918 {'fromType': constantType, 'toType': elementType});
919 });
920 }
921 }
922 }
923
924 void updateFieldValue(Node node,
925 TypedElement element,
926 AstConstant constant) {
927 potentiallyCheckType(node, element, constant);
928 fieldValues[element] = constant;
929 }
930
931 /**
932 * Given the arguments (a list of constants) assigns them to the parameters,
933 * updating the definitions map. If the constructor has field-initializer
934 * parameters (like [:this.x:]), also updates the [fieldValues] map.
935 */
936 void assignArgumentsToParameters(List<AstConstant> arguments) {
937 // Assign arguments to parameters.
938 FunctionSignature signature = constructor.functionSignature;
939 int index = 0;
940 signature.orderedForEachParameter((ParameterElement parameter) {
941 AstConstant argument = arguments[index++];
942 Node node = parameter.node;
943 if (parameter.isInitializingFormal) {
944 InitializingFormalElement initializingFormal = parameter;
945 updateFieldValue(node, initializingFormal.fieldElement, argument);
946 } else {
947 potentiallyCheckType(node, parameter, argument);
948 definitions[parameter] = argument;
949 }
950 });
951 }
952
953 void evaluateSuperOrRedirectSend(List<AstConstant> compiledArguments,
954 FunctionElement targetConstructor) {
955 ConstructorEvaluator evaluator = new ConstructorEvaluator(
956 constructedType.asInstanceOf(targetConstructor.enclosingClass),
957 targetConstructor, handler, compiler);
958 evaluator.evaluateConstructorFieldValues(compiledArguments);
959 // Copy over the fieldValues from the super/redirect-constructor.
960 // No need to go through [updateFieldValue] because the
961 // assignments have already been checked in checked mode.
962 evaluator.fieldValues.forEach((key, value) => fieldValues[key] = value);
963 }
964
965 /**
966 * Runs through the initializers of the given [constructor] and updates
967 * the [fieldValues] map.
968 */
969 void evaluateConstructorInitializers() {
970 if (constructor.isSynthesized) {
971 List<AstConstant> compiledArguments = <AstConstant>[];
972
973 Function compileArgument = (element) => definitions[element];
974 Function compileConstant = handler.compileConstant;
975 FunctionElement target = constructor.definingConstructor.implementation;
976 Selector.addForwardingElementArgumentsToList(constructor,
977 compiledArguments,
978 target,
979 compileArgument,
980 compileConstant,
981 compiler.world);
982 evaluateSuperOrRedirectSend(compiledArguments, target);
983 return;
984 }
985 FunctionExpression functionNode = constructor.node;
986 NodeList initializerList = functionNode.initializers;
987
988 bool foundSuperOrRedirect = false;
989
990 if (initializerList != null) {
991 for (Link<Node> link = initializerList.nodes;
992 !link.isEmpty;
993 link = link.tail) {
994 assert(link.head is Send);
995 if (link.head is !SendSet) {
996 // A super initializer or constructor redirection.
997 Send call = link.head;
998 FunctionElement target = elements[call];
999 List<AstConstant> compiledArguments =
1000 evaluateArgumentsToConstructor(
1001 call, elements.getSelector(call), call.arguments, target,
1002 compileArgument: evaluateConstant);
1003 evaluateSuperOrRedirectSend(compiledArguments, target);
1004 foundSuperOrRedirect = true;
1005 } else {
1006 // A field initializer.
1007 SendSet init = link.head;
1008 Link<Node> initArguments = init.arguments;
1009 assert(!initArguments.isEmpty && initArguments.tail.isEmpty);
1010 AstConstant fieldValue = evaluate(initArguments.head);
1011 updateFieldValue(init, elements[init], fieldValue);
1012 }
1013 }
1014 }
1015
1016 if (!foundSuperOrRedirect) {
1017 // No super initializer found. Try to find the default constructor if
1018 // the class is not Object.
1019 ClassElement enclosingClass = constructor.enclosingClass;
1020 ClassElement superClass = enclosingClass.superclass;
1021 if (enclosingClass != compiler.objectClass) {
1022 assert(superClass != null);
1023 assert(superClass.resolutionState == STATE_DONE);
1024
1025 Selector selector =
1026 new Selector.callDefaultConstructor(enclosingClass.library);
1027
1028 FunctionElement targetConstructor =
1029 superClass.lookupConstructor(selector);
1030 if (targetConstructor == null) {
1031 compiler.internalError(functionNode,
1032 "No default constructor available.");
1033 }
1034 List<AstConstant> compiledArguments =
1035 evaluateArgumentsToConstructor(
1036 functionNode, selector, const Link<Node>(), targetConstructor);
1037 evaluateSuperOrRedirectSend(compiledArguments, targetConstructor);
1038 }
1039 }
1040 }
1041
1042 /**
1043 * Simulates the execution of the [constructor] with the given
1044 * [arguments] to obtain the field values that need to be passed to the
1045 * native JavaScript constructor.
1046 */
1047 void evaluateConstructorFieldValues(List<AstConstant> arguments) {
1048 compiler.withCurrentElement(constructor, () {
1049 assignArgumentsToParameters(arguments);
1050 evaluateConstructorInitializers();
1051 });
1052 }
1053
1054 /// Builds a normalized list of the constant values for each field in the
1055 /// inheritance chain of [classElement].
1056 List<AstConstant> buildFieldConstants(ClassElement classElement) {
1057 List<AstConstant> fieldConstants = <AstConstant>[];
1058 classElement.implementation.forEachInstanceField(
1059 (ClassElement enclosing, FieldElement field) {
1060 AstConstant fieldValue = fieldValues[field];
1061 if (fieldValue == null) {
1062 // Use the default value.
1063 fieldValue = new AstConstant.fromDefaultValue(
1064 field, handler.compileConstant(field));
1065 }
1066 fieldConstants.add(fieldValue);
1067 },
1068 includeSuperAndInjectedMembers: true);
1069 return fieldConstants;
1070 }
1071 }
1072
1073 /// A constant created from the front-end AST.
1074 ///
1075 /// [element] and [node] point to the source location of the constant.
1076 /// [expression] holds the symbolic constant expression and [value] its constant
1077 /// value.
1078 ///
1079 /// This class differs from [ConstantExpression] in that it is coupled to the
1080 /// front-end AST whereas [ConstantExpression] is only coupled to the element
1081 /// model.
1082 class AstConstant {
1083 final Element element;
1084 final Node node;
1085 final ConstantExpression expression;
1086
1087 AstConstant(this.element, this.node, this.expression);
1088
1089 factory AstConstant.fromDefaultValue(
1090 VariableElement element,
1091 ConstantExpression constant) {
1092 return new AstConstant(
1093 element,
1094 element.initializer != null ? element.initializer : element.node,
1095 constant);
1096 }
1097
1098 ConstantValue get value => expression.value;
1099
1100 String toString() => expression.toString();
1101 }
OLDNEW
« no previous file with comments | « sdk/lib/_internal/compiler/implementation/common.dart ('k') | sdk/lib/_internal/compiler/implementation/compiler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698