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