| 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 |