OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** | |
6 * A simple code analyzer for Dart. | |
7 * | |
8 * Currently used to ensure all concrete generic types are visited. | |
9 * Also performs all static type checks - so these don't need to be | |
10 * done in later phases. | |
11 * | |
12 * Ultimately, this should include abstract interpreter work. This will | |
13 * result in an interesting split beteen this class and MethodGenerator | |
14 * which should be turned into nothing more than a code generator. | |
15 */ | |
16 // TODO(jimhug): This class shares too much code with MethodGenerator. | |
17 class MethodAnalyzer implements TreeVisitor { | |
18 MethodMember method; | |
19 Statement body; | |
20 | |
21 CallFrame _frame; | |
22 | |
23 /** | |
24 * Track whether or not [body] refers to any type parameters from the | |
25 * enclosing type to advise future code generation and analysis. | |
26 */ | |
27 bool hasTypeParams = false; | |
28 | |
29 MethodAnalyzer(this.method, this.body); | |
30 | |
31 // TODO(jimhug): Type issue with requiring CallFrame here... | |
32 void analyze(CallFrame context) { | |
33 var thisValue; | |
34 // TODO(jimhug): Move Constructor analysis to here and below. | |
35 | |
36 if (context != null) { | |
37 thisValue = context.thisValue; | |
38 } else { | |
39 thisValue = new PureStaticValue(method.declaringType, null); | |
40 } | |
41 var values = []; | |
42 for (var p in method.parameters) { | |
43 values.add(new PureStaticValue(p.type, null)); | |
44 } | |
45 var args = new Arguments(null, values); | |
46 | |
47 _frame = new CallFrame(this, method, thisValue, args, context); | |
48 _bindArguments(_frame.args); | |
49 | |
50 // Visit the super or this call in a constructor, if any. | |
51 final declaredInitializers = method.definition.dynamic.initializers; | |
52 if (declaredInitializers != null) { | |
53 for (var init in declaredInitializers) { | |
54 if (init is CallExpression) { | |
55 visitCallExpression(init, true); | |
56 } | |
57 } | |
58 } | |
59 | |
60 if (body != null) body.visit(this); | |
61 } | |
62 | |
63 /* Checks whether or not a particular TypeReference Node includes references | |
64 * to type parameters. */ | |
65 bool _hasTypeParams(node) { | |
66 if (node is NameTypeReference) { | |
67 var name = node.name.name; | |
68 return (method.declaringType.lookupTypeParam(name) != null); | |
69 } else if (node is GenericTypeReference) { | |
70 for (var typeArg in node.typeArguments) { | |
71 if (_hasTypeParams(typeArg)) return true; | |
72 } | |
73 return false; | |
74 } else { | |
75 // TODO(jimhug): Do we need to include FunctionTypeReference here? | |
76 return false; | |
77 } | |
78 } | |
79 | |
80 Type resolveType(TypeReference node, bool typeErrors, | |
81 bool allowTypeParams) { | |
82 if (!hasTypeParams && _hasTypeParams(node)) { | |
83 hasTypeParams = true; | |
84 } | |
85 return method.resolveType(node, typeErrors, allowTypeParams); | |
86 } | |
87 | |
88 | |
89 Value makeNeutral(Value inValue, SourceSpan span) { | |
90 return new PureStaticValue(inValue.type, span); | |
91 } | |
92 | |
93 void _bindArguments(Arguments args) { | |
94 for (int i = 0; i < method.parameters.length; i++) { | |
95 var p = method.parameters[i]; | |
96 Value currentArg = null; | |
97 if (i < args.bareCount) { | |
98 currentArg = args.values[i]; | |
99 } else { | |
100 // Handle named or missing arguments | |
101 currentArg = args.getValue(p.name); | |
102 if (currentArg === null) { | |
103 // TODO(jmesserly): this path won't work if we ever get here, because | |
104 // Paramter.genValue assumes it has a MethodGenerator. | |
105 | |
106 // Ensure default value for param has been generated | |
107 p.genValue(method, _frame); // TODO(jimhug): Needed here? | |
108 if (p.value === null) { | |
109 world.warning('missing argument at call - does not match'); | |
110 } | |
111 currentArg = p.value; | |
112 } | |
113 } | |
114 | |
115 currentArg = makeNeutral(currentArg, p.definition.span); | |
116 | |
117 // TODO(jimhug): Add checks for constructor initializers. | |
118 _frame.declareParameter(p, currentArg); | |
119 // TODO(jimhug): Add type check? | |
120 } | |
121 } | |
122 | |
123 visitBool(Expression node) { | |
124 return visitTypedValue(node, world.boolType); | |
125 } | |
126 | |
127 visitValue(Expression node) { | |
128 if (node == null) return null; | |
129 | |
130 var value = node.visit(this); | |
131 value.checkFirstClass(node.span); | |
132 return value; | |
133 } | |
134 | |
135 /** | |
136 * Visit [node] and ensure statically or with an runtime check that it has | |
137 * the expected type (if specified). | |
138 */ | |
139 visitTypedValue(Expression node, Type expectedType) { | |
140 final val = visitValue(node); | |
141 return val === null ? null : val.convertTo(_frame, expectedType); | |
142 } | |
143 | |
144 visitVoid(Expression node) { | |
145 // TODO(jimhug): Add some helpful diagnostics for silly void uses. | |
146 return visitValue(node); | |
147 } | |
148 | |
149 | |
150 Arguments _visitArgs(List<ArgumentNode> arguments) { | |
151 var args = []; | |
152 bool seenLabel = false; | |
153 for (var arg in arguments) { | |
154 if (arg.label != null) { | |
155 seenLabel = true; | |
156 } else if (seenLabel) { | |
157 // TODO(jimhug): Move this into parser? | |
158 world.error('bare argument cannot follow named arguments', arg.span); | |
159 } | |
160 args.add(visitValue(arg.value)); | |
161 } | |
162 | |
163 return new Arguments(arguments, args); | |
164 } | |
165 | |
166 MethodMember _makeLambdaMethod(String name, FunctionDefinition func) { | |
167 var meth = new MethodMember.lambda(name, method.declaringType, func); | |
168 meth.enclosingElement = method; | |
169 meth._methodData = new MethodData(meth, _frame); | |
170 meth.resolve(); | |
171 return meth; | |
172 } | |
173 | |
174 void _pushBlock(Node node) { | |
175 _frame.pushBlock(node); | |
176 } | |
177 void _popBlock(Node node) { | |
178 _frame.popBlock(node); | |
179 } | |
180 | |
181 Member _resolveBare(String name, Node node) { | |
182 var type = _frame.method.declaringType; | |
183 var member = type.getMember(name); | |
184 if (member == null || member.declaringType != type) { | |
185 var libMember = _frame.library.lookup(name, node.span); | |
186 if (libMember !== null) return libMember; | |
187 } | |
188 | |
189 if (member !== null && !member.isStatic && _frame.isStatic) { | |
190 world.error('cannot refer to instance member from static method', | |
191 node.span); | |
192 } | |
193 return member; | |
194 } | |
195 | |
196 // ******************* Statements ******************* | |
197 | |
198 void visitDietStatement(DietStatement node) { | |
199 var parser = new Parser(node.span.file, startOffset: node.span.start); | |
200 parser.block().visit(this); | |
201 } | |
202 | |
203 void visitVariableDefinition(VariableDefinition node) { | |
204 var isFinal = false; | |
205 // TODO(jimhug): Clean this up and share modifier parsing somewhere. | |
206 if (node.modifiers != null && node.modifiers[0].kind == TokenKind.FINAL) { | |
207 isFinal = true; | |
208 } | |
209 var type = resolveType(node.type, false, true); | |
210 for (int i=0; i < node.names.length; i++) { | |
211 final name = node.names[i].name; | |
212 var value = visitValue(node.values[i]); | |
213 _frame.create(name, type, node.names[i], isFinal, value); | |
214 } | |
215 } | |
216 | |
217 void visitFunctionDefinition(FunctionDefinition node) { | |
218 var meth = _makeLambdaMethod(node.name.name, node); | |
219 // TODO(jimhug): Better FunctionValue that tracks actual function | |
220 var funcValue = _frame.create(meth.name, meth.functionType, | |
221 method.definition, true, null); | |
222 | |
223 meth.methodData.analyze(); | |
224 } | |
225 | |
226 | |
227 void visitReturnStatement(ReturnStatement node) { | |
228 if (node.value == null) { | |
229 _frame.returns(Value.fromNull(node.span)); | |
230 } else { | |
231 _frame.returns(visitValue(node.value)); | |
232 } | |
233 } | |
234 | |
235 void visitThrowStatement(ThrowStatement node) { | |
236 // Dart allows throwing anything, just like JS | |
237 if (node.value != null) { | |
238 var value = visitValue(node.value); | |
239 } else { | |
240 // skip | |
241 } | |
242 } | |
243 | |
244 void visitAssertStatement(AssertStatement node) { | |
245 // be sure to walk test for static checking even is asserts disabled | |
246 var test = visitValue(node.test); // TODO(jimhug): check bool or callable. | |
247 } | |
248 | |
249 void visitBreakStatement(BreakStatement node) { | |
250 } | |
251 | |
252 void visitContinueStatement(ContinueStatement node) { | |
253 } | |
254 | |
255 void visitIfStatement(IfStatement node) { | |
256 var test = visitBool(node.test); | |
257 node.trueBranch.visit(this); | |
258 if (node.falseBranch != null) { | |
259 node.falseBranch.visit(this); | |
260 } | |
261 } | |
262 | |
263 void visitWhileStatement(WhileStatement node) { | |
264 var test = visitBool(node.test); | |
265 node.body.visit(this); | |
266 } | |
267 | |
268 void visitDoStatement(DoStatement node) { | |
269 node.body.visit(this); | |
270 var test = visitBool(node.test); | |
271 } | |
272 | |
273 void visitForStatement(ForStatement node) { | |
274 _pushBlock(node); | |
275 if (node.init != null) node.init.visit(this); | |
276 | |
277 if (node.test != null) { | |
278 var test = visitBool(node.test); | |
279 } | |
280 for (var s in node.step) { | |
281 var sv = visitVoid(s); | |
282 } | |
283 | |
284 _pushBlock(node.body); | |
285 node.body.visit(this); | |
286 _popBlock(node.body); | |
287 | |
288 | |
289 _popBlock(node); | |
290 } | |
291 | |
292 void visitForInStatement(ForInStatement node) { | |
293 // TODO(jimhug): visitValue and other cleanups here. | |
294 var itemType = resolveType(node.item.type, false, true); | |
295 var list = node.list.visit(this); | |
296 _visitForInBody(node, itemType, list); | |
297 } | |
298 | |
299 | |
300 bool _isFinal(typeRef) { | |
301 if (typeRef is GenericTypeReference) { | |
302 typeRef = typeRef.baseType; | |
303 } else if (typeRef is SimpleTypeReference) { | |
304 return false; | |
305 } | |
306 return typeRef != null && typeRef.isFinal; | |
307 } | |
308 | |
309 void _visitForInBody(ForInStatement node, Type itemType, Value list) { | |
310 // TODO(jimhug): Check that itemType matches list members... | |
311 _pushBlock(node); | |
312 | |
313 bool isFinal = _isFinal(node.item.type); | |
314 var itemName = node.item.name.name; | |
315 var item = | |
316 _frame.create(itemName, itemType, node.item.name, isFinal, null); | |
317 | |
318 var iterator = | |
319 list.invoke(_frame, 'iterator', node.list, Arguments.EMPTY); | |
320 | |
321 node.body.visit(this); | |
322 _popBlock(node); | |
323 } | |
324 | |
325 _createDI(DeclaredIdentifier di) { | |
326 _frame.create(di.name.name, resolveType(di.type, false, true), di.name, | |
327 true, null); | |
328 } | |
329 | |
330 void visitTryStatement(TryStatement node) { | |
331 _pushBlock(node.body); | |
332 node.body.visit(this); | |
333 _popBlock(node.body); | |
334 if (node.catches.length > 0) { | |
335 for (int i = 0; i < node.catches.length; i++) { | |
336 var catch_ = node.catches[i]; | |
337 _pushBlock(catch_); | |
338 _createDI(catch_.exception); | |
339 if (catch_.trace !== null) { | |
340 _createDI(catch_.trace); | |
341 } | |
342 catch_.body.visit(this); | |
343 _popBlock(catch_); | |
344 } | |
345 } | |
346 | |
347 if (node.finallyBlock != null) { | |
348 node.finallyBlock.visit(this); | |
349 } | |
350 } | |
351 | |
352 void visitSwitchStatement(SwitchStatement node) { | |
353 var test = visitValue(node.test); | |
354 for (var case_ in node.cases) { | |
355 _pushBlock(case_); | |
356 | |
357 for (int i=0; i < case_.cases.length; i++) { | |
358 var expr = case_.cases[i]; | |
359 if (expr == null) { | |
360 //skip | |
361 } else { | |
362 var value = visitValue(expr); | |
363 } | |
364 } | |
365 _visitAllStatements(case_.statements); | |
366 _popBlock(case_); | |
367 } | |
368 } | |
369 | |
370 _visitAllStatements(statementList) { | |
371 for (int i = 0; i < statementList.length; i++) { | |
372 var stmt = statementList[i]; | |
373 stmt.visit(this); | |
374 } | |
375 } | |
376 | |
377 void visitBlockStatement(BlockStatement node) { | |
378 _pushBlock(node); | |
379 _visitAllStatements(node.body); | |
380 _popBlock(node); | |
381 } | |
382 | |
383 void visitLabeledStatement(LabeledStatement node) { | |
384 node.body.visit(this); | |
385 } | |
386 | |
387 void visitExpressionStatement(ExpressionStatement node) { | |
388 var value = visitVoid(node.body); | |
389 } | |
390 | |
391 void visitEmptyStatement(EmptyStatement node) { | |
392 } | |
393 | |
394 | |
395 | |
396 // ******************* Expressions ******************* | |
397 visitLambdaExpression(LambdaExpression node) { | |
398 var name = (node.func.name != null) ? node.func.name.name : ''; | |
399 | |
400 MethodMember meth = _makeLambdaMethod(name, node.func); | |
401 // TODO(jimhug): Worry about proper scope for recursive lambda. | |
402 meth.methodData.analyze(); | |
403 | |
404 return _frame._makeValue(world.functionType, node); | |
405 } | |
406 | |
407 analyzeInitializerConstructorCall(CallExpression node, | |
408 Expression receiver, | |
409 String name) { | |
410 var type = _frame.method.declaringType; | |
411 if (receiver is SuperExpression) { | |
412 type = type.parent; | |
413 } | |
414 var member = type.getConstructor(name == null ? '' : name); | |
415 if (member !== null) { | |
416 return member.invoke(_frame, node, _frame.makeThisValue(node), | |
417 _visitArgs(node.arguments)); | |
418 } else { | |
419 String constructorName = name == null ? '' : '.$name'; | |
420 world.warning('cannot find constructor "${type.name}$constructorName"', | |
421 node.span); | |
422 return _frame._makeValue(world.varType, node); | |
423 } | |
424 } | |
425 | |
426 bool isThisOrSuper(Expression node) { | |
427 return node is ThisExpression || node is SuperExpression; | |
428 } | |
429 | |
430 Value visitCallExpression(CallExpression node, | |
431 [bool visitingInitializers = false]) { | |
432 var target; | |
433 var position = node.target; | |
434 var name = ':call'; | |
435 if (node.target is DotExpression) { | |
436 DotExpression dot = node.target; | |
437 target = dot.self.visit(this); | |
438 name = dot.name.name; | |
439 if (isThisOrSuper(dot.self) && visitingInitializers) { | |
440 return analyzeInitializerConstructorCall(node, dot.self, name); | |
441 } else { | |
442 position = dot.name; | |
443 } | |
444 } else if (isThisOrSuper(node.target) && visitingInitializers) { | |
445 return analyzeInitializerConstructorCall(node, node.target, null); | |
446 } else if (node.target is VarExpression) { | |
447 VarExpression varExpr = node.target; | |
448 name = varExpr.name.name; | |
449 // First check in block scopes. | |
450 target = _frame.lookup(name); | |
451 if (target != null) { | |
452 return target.get(position).invoke(_frame, ':call', node, | |
453 _visitArgs(node.arguments)); | |
454 } | |
455 | |
456 var member = _resolveBare(name, varExpr.name); | |
457 if (member !== null) { | |
458 return member.invoke(_frame, node, _frame.makeThisValue(node), | |
459 _visitArgs(node.arguments)); | |
460 } else { | |
461 world.warning('cannot find "$name"', node.span); | |
462 return _frame._makeValue(world.varType, node); | |
463 } | |
464 } else { | |
465 target = node.target.visit(this); | |
466 } | |
467 | |
468 return target.invoke(_frame, name, position, _visitArgs(node.arguments)); | |
469 } | |
470 | |
471 Value visitIndexExpression(IndexExpression node) { | |
472 var target = visitValue(node.target); | |
473 var index = visitValue(node.index); | |
474 | |
475 return target.invoke(_frame, ':index', node, | |
476 new Arguments(null, [index])); | |
477 } | |
478 | |
479 | |
480 Value visitBinaryExpression(BinaryExpression node, [bool isVoid = false]) { | |
481 final kind = node.op.kind; | |
482 | |
483 if (kind == TokenKind.AND || kind == TokenKind.OR) { | |
484 var xb = visitBool(node.x); | |
485 var yb = visitBool(node.y); | |
486 return xb.binop(kind, yb, _frame, node); | |
487 } | |
488 | |
489 final assignKind = TokenKind.kindFromAssign(node.op.kind); | |
490 if (assignKind == -1) { | |
491 final x = visitValue(node.x); | |
492 final y = visitValue(node.y); | |
493 return x.binop(kind, y, _frame, node); | |
494 } else { | |
495 return _visitAssign(assignKind, node.x, node.y, node); | |
496 } | |
497 } | |
498 | |
499 /** | |
500 * Visits an assignment expression. | |
501 */ | |
502 Value _visitAssign(int kind, Expression xn, Expression yn, Node position) { | |
503 if (xn is VarExpression) { | |
504 return _visitVarAssign(kind, xn, yn, position); | |
505 } else if (xn is IndexExpression) { | |
506 return _visitIndexAssign(kind, xn, yn, position); | |
507 } else if (xn is DotExpression) { | |
508 return _visitDotAssign(kind, xn, yn, position); | |
509 } else { | |
510 world.error('illegal lhs', xn.span); | |
511 } | |
512 } | |
513 | |
514 _visitVarAssign(int kind, VarExpression xn, Expression yn, Node node) { | |
515 final value = visitValue(yn); | |
516 final name = xn.name.name; | |
517 | |
518 // First check in block scopes. | |
519 var slot = _frame.lookup(name); | |
520 if (slot != null) { | |
521 slot.set(value); | |
522 } else { | |
523 var member = _resolveBare(name, xn.name); | |
524 if (member !== null) { | |
525 member._set(_frame, node, _frame.makeThisValue(node), value); | |
526 } else { | |
527 world.warning('cannot find "$name"', node.span); | |
528 } | |
529 } | |
530 return _frame._makeValue(value.type, node); | |
531 } | |
532 | |
533 _visitIndexAssign(int kind, IndexExpression xn, Expression yn, | |
534 Node position) { | |
535 var target = visitValue(xn.target); | |
536 var index = visitValue(xn.index); | |
537 var y = visitValue(yn); | |
538 | |
539 return target.setIndex(_frame, index, position, y, kind: kind); | |
540 } | |
541 | |
542 _visitDotAssign(int kind, DotExpression xn, Expression yn, Node position) { | |
543 // This is not visitValue because types members are assignable. | |
544 var target = xn.self.visit(this); | |
545 var y = visitValue(yn); | |
546 | |
547 return target.set_(_frame, xn.name.name, xn.name, y, kind: kind); | |
548 } | |
549 | |
550 visitUnaryExpression(UnaryExpression node) { | |
551 var value = visitValue(node.self); | |
552 switch (node.op.kind) { | |
553 case TokenKind.INCR: | |
554 case TokenKind.DECR: | |
555 return value.binop(TokenKind.ADD, | |
556 _frame._makeValue(world.intType, node), _frame, node); | |
557 } | |
558 return value.unop(node.op.kind, _frame, node); | |
559 } | |
560 | |
561 visitDeclaredIdentifier(DeclaredIdentifier node) { | |
562 world.error('Expected expression', node.span); | |
563 } | |
564 | |
565 visitAwaitExpression(AwaitExpression node) { | |
566 world.internalError( | |
567 'Await expressions should have been eliminated before code generation', | |
568 node.span); | |
569 } | |
570 | |
571 Value visitPostfixExpression(PostfixExpression node, | |
572 [bool isVoid = false]) { | |
573 var value = visitValue(node.body); | |
574 | |
575 return _frame._makeValue(value.type, node); | |
576 } | |
577 | |
578 Value visitNewExpression(NewExpression node) { | |
579 var typeRef = node.type; | |
580 | |
581 var constructorName = ''; | |
582 if (node.name != null) { | |
583 constructorName = node.name.name; | |
584 } | |
585 | |
586 // Named constructors and library prefixes, oh my! | |
587 // At last, we can collapse the ambiguous wave function... | |
588 if (constructorName == '' && typeRef is NameTypeReference && | |
589 typeRef.names != null) { | |
590 | |
591 // Pull off the last name from the type, guess it's the constructor name. | |
592 var names = new List.from(typeRef.names); | |
593 constructorName = names.removeLast().name; | |
594 if (names.length == 0) names = null; | |
595 | |
596 typeRef = new NameTypeReference( | |
597 typeRef.isFinal, typeRef.name, names, typeRef.span); | |
598 } | |
599 | |
600 var type = resolveType(typeRef, true, true); | |
601 if (type.isTop) { | |
602 type = type.library.findTypeByName(constructorName); | |
603 if (type == null) { | |
604 world.error('cannot resolve type $constructorName', node.span); | |
605 } | |
606 constructorName = ''; | |
607 } | |
608 | |
609 if (type is ParameterType) { | |
610 world.error('cannot instantiate a type parameter', node.span); | |
611 return _frame._makeValue(world.varType, node); | |
612 } | |
613 | |
614 var m = type.getConstructor(constructorName); | |
615 if (m == null) { | |
616 var name = type.jsname; | |
617 if (type.isVar) { | |
618 name = typeRef.name.name; | |
619 } | |
620 world.warning('no matching constructor for $name', node.span); | |
621 return _frame._makeValue(type, node); | |
622 } | |
623 | |
624 if (node.isConst) { | |
625 if (!m.isConst) { | |
626 world.error('can\'t use const on a non-const constructor', node.span); | |
627 } | |
628 for (var arg in node.arguments) { | |
629 // TODO(jimhug): Remove this double walk of arguments. | |
630 if (!visitValue(arg.value).isConst) { | |
631 world.error('const constructor expects const arguments', arg.span); | |
632 } | |
633 } | |
634 } | |
635 | |
636 | |
637 var args = _visitArgs(node.arguments); | |
638 var target = new TypeValue(type, typeRef.span); | |
639 return new PureStaticValue(type, node.span, node.isConst); | |
640 } | |
641 | |
642 Value visitListExpression(ListExpression node) { | |
643 var argValues = []; | |
644 var listType = world.listType; | |
645 var type = world.varType; | |
646 if (node.itemType != null) { | |
647 type = resolveType(node.itemType, true, !node.isConst); | |
648 if (node.isConst && (type is ParameterType || type.hasTypeParams)) { | |
649 world.error('type parameter cannot be used in const list literals'); | |
650 } | |
651 listType = listType.getOrMakeConcreteType([type]); | |
652 } | |
653 for (var item in node.values) { | |
654 var arg = visitTypedValue(item, type); | |
655 argValues.add(arg); | |
656 // TODO(jimhug): Reenable these checks here - and remove from MethodGen | |
657 //if (node.isConst && !arg.isConst) { | |
658 // world.error('const list can only contain const values', arg.span); | |
659 //} | |
660 } | |
661 | |
662 world.listFactoryType.markUsed(); | |
663 | |
664 return new PureStaticValue(listType, node.span, node.isConst); | |
665 } | |
666 | |
667 | |
668 Value visitMapExpression(MapExpression node) { | |
669 var values = <Value>[]; | |
670 var valueType = world.varType, keyType = world.stringType; | |
671 var mapType = world.mapType; // TODO(jimhug): immutable type? | |
672 if (node.valueType !== null) { | |
673 if (node.keyType !== null) { | |
674 keyType = method.resolveType(node.keyType, true, !node.isConst); | |
675 // TODO(jimhug): Would be nice to allow arbitrary keys here (this is | |
676 // currently not allowed by the spec). | |
677 if (!keyType.isString) { | |
678 world.error('the key type of a map literal must be "String"', | |
679 keyType.span); | |
680 } | |
681 if (node.isConst && | |
682 (keyType is ParameterType || keyType.hasTypeParams)) { | |
683 world.error('type parameter cannot be used in const map literals'); | |
684 } | |
685 } | |
686 | |
687 valueType = resolveType(node.valueType, true, !node.isConst); | |
688 if (node.isConst && | |
689 (valueType is ParameterType || valueType.hasTypeParams)) { | |
690 world.error('type parameter cannot be used in const map literals'); | |
691 } | |
692 | |
693 mapType = mapType.getOrMakeConcreteType([keyType, valueType]); | |
694 } | |
695 | |
696 for (int i = 0; i < node.items.length; i += 2) { | |
697 var key = visitTypedValue(node.items[i], keyType); | |
698 // TODO(jimhug): Reenable these checks here - and remove from MethodGen | |
699 //if (node.isConst && !key.isConst) { | |
700 // world.error('const map can only contain const keys', key.span); | |
701 //} | |
702 values.add(key); | |
703 | |
704 var value = visitTypedValue(node.items[i + 1], valueType); | |
705 if (node.isConst && !value.isConst) { | |
706 world.error('const map can only contain const values', value.span); | |
707 } | |
708 values.add(value); | |
709 } | |
710 | |
711 return new PureStaticValue(mapType, node.span, node.isConst); | |
712 } | |
713 | |
714 | |
715 Value visitConditionalExpression(ConditionalExpression node) { | |
716 var test = visitBool(node.test); | |
717 var trueBranch = visitValue(node.trueBranch); | |
718 var falseBranch = visitValue(node.falseBranch); | |
719 | |
720 // TODO(jimhug): Should be unioning values, not just types. | |
721 return _frame._makeValue(Type.union(trueBranch.type, falseBranch.type), | |
722 node); | |
723 } | |
724 | |
725 Value visitIsExpression(IsExpression node) { | |
726 var value = visitValue(node.x); | |
727 var type = resolveType(node.type, false, true); | |
728 return _frame._makeValue(world.boolType, node); | |
729 } | |
730 | |
731 Value visitParenExpression(ParenExpression node) { | |
732 return visitValue(node.body); | |
733 } | |
734 | |
735 Value visitDotExpression(DotExpression node) { | |
736 // Types are legal targets of . | |
737 var target = node.self.visit(this); | |
738 return target.get_(_frame, node.name.name, node); | |
739 } | |
740 | |
741 | |
742 Value visitVarExpression(VarExpression node) { | |
743 final name = node.name.name; | |
744 | |
745 // First check in block scopes. | |
746 var slot = _frame.lookup(name); | |
747 if (slot != null) { | |
748 return slot.get(node); | |
749 } | |
750 | |
751 var member = _resolveBare(name, node.name); | |
752 if (member !== null) { | |
753 if (member is TypeMember) { | |
754 return new PureStaticValue(member.dynamic.type, node.span, true, | |
755 true); | |
756 } else { | |
757 return member._get(_frame, node, _frame.makeThisValue(node)); | |
758 } | |
759 } else { | |
760 world.warning('cannot find "$name"', node.span); | |
761 return _frame._makeValue(world.varType, node); | |
762 } | |
763 } | |
764 | |
765 Value visitThisExpression(ThisExpression node) { | |
766 return _frame.makeThisValue(node); | |
767 } | |
768 | |
769 Value visitSuperExpression(SuperExpression node) { | |
770 return _frame.makeSuperValue(node); | |
771 } | |
772 | |
773 Value visitLiteralExpression(LiteralExpression node) { | |
774 return new PureStaticValue(node.value.type, node.span, true); | |
775 } | |
776 | |
777 Value visitStringConcatExpression(StringConcatExpression node) { | |
778 bool isConst = true; | |
779 node.strings.forEach((each) { | |
780 if (!visitValue(each).isConst) isConst = false; | |
781 }); | |
782 return new PureStaticValue(world.stringType, node.span, isConst); | |
783 } | |
784 | |
785 Value visitStringInterpExpression(StringInterpExpression node) { | |
786 bool isConst = true; | |
787 node.pieces.forEach((each) { | |
788 if (!visitValue(each).isConst) isConst = false; | |
789 }); | |
790 return new PureStaticValue(world.stringType, node.span, isConst); | |
791 } | |
792 } | |
793 | |
OLD | NEW |