| 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 #import("../../../../lib/compiler/implementation/leg.dart"); | |
| 6 #import("../../../../lib/compiler/implementation/elements/elements.dart"); | |
| 7 #import("../../../../lib/compiler/implementation/tree/tree.dart"); | |
| 8 #import("../../../../lib/compiler/implementation/util/util.dart"); | |
| 9 #import("mock_compiler.dart"); | |
| 10 #import("parser_helper.dart"); | |
| 11 | |
| 12 Node buildIdentifier(String name) => new Identifier(scan(name)); | |
| 13 | |
| 14 Node buildInitialization(String name) => | |
| 15 parseBodyCode('$name = 1', | |
| 16 (parser, tokens) => parser.parseOptionallyInitializedIdentifier(tokens)); | |
| 17 | |
| 18 createLocals(List variables) { | |
| 19 var locals = []; | |
| 20 for (final variable in variables) { | |
| 21 String name = variable[0]; | |
| 22 bool init = variable[1]; | |
| 23 if (init) { | |
| 24 locals.add(buildInitialization(name)); | |
| 25 } else { | |
| 26 locals.add(buildIdentifier(name)); | |
| 27 } | |
| 28 } | |
| 29 var definitions = new NodeList(null, new Link.fromList(locals), null, null); | |
| 30 return new VariableDefinitions(null, null, definitions, null); | |
| 31 } | |
| 32 | |
| 33 testLocals(List variables) { | |
| 34 MockCompiler compiler = new MockCompiler(); | |
| 35 ResolverVisitor visitor = compiler.resolverVisitor(); | |
| 36 Element element = visitor.visit(createLocals(variables)); | |
| 37 // A VariableDefinitions does not have an element. | |
| 38 Expect.equals(null, element); | |
| 39 Expect.equals(variables.length, map(visitor).length); | |
| 40 | |
| 41 for (final variable in variables) { | |
| 42 final name = variable[0]; | |
| 43 Identifier id = buildIdentifier(name); | |
| 44 final VariableElement variableElement = visitor.visit(id); | |
| 45 MethodScope scope = visitor.context; | |
| 46 Expect.equals(variableElement, scope.elements[buildSourceString(name)]); | |
| 47 } | |
| 48 return compiler; | |
| 49 } | |
| 50 | |
| 51 main() { | |
| 52 testLocalsOne(); | |
| 53 testLocalsTwo(); | |
| 54 testLocalsThree(); | |
| 55 testLocalsFour(); | |
| 56 testLocalsFive(); | |
| 57 testParametersOne(); | |
| 58 testFor(); | |
| 59 testTypeAnnotation(); | |
| 60 testSuperclass(); | |
| 61 // testVarSuperclass(); // The parser crashes with 'class Foo extends var'. | |
| 62 // testOneInterface(); // Generates unexpected error message. | |
| 63 // testTwoInterfaces(); // Generates unexpected error message. | |
| 64 testFunctionExpression(); | |
| 65 testNewExpression(); | |
| 66 testTopLevelFields(); | |
| 67 testClassHierarchy(); | |
| 68 testInitializers(); | |
| 69 testThis(); | |
| 70 testSuperCalls(); | |
| 71 testTypeVariables(); | |
| 72 } | |
| 73 | |
| 74 testTypeVariables() { | |
| 75 matchResolvedTypes(visitor, text, name, expectedElements) { | |
| 76 VariableDefinitions definition = parseStatement(text); | |
| 77 visitor.visit(definition.type); | |
| 78 InterfaceType type = visitor.mapping.getType(definition.type); | |
| 79 Expect.equals(definition.type.typeArguments.length(), | |
| 80 length(type.arguments)); | |
| 81 int index = 0; | |
| 82 Link<Type> arguments = type.arguments; | |
| 83 while (!arguments.isEmpty()) { | |
| 84 Expect.equals(true, index < expectedElements.length); | |
| 85 Expect.equals(expectedElements[index], arguments.head.element); | |
| 86 index++; | |
| 87 arguments = arguments.tail; | |
| 88 } | |
| 89 Expect.equals(index, expectedElements.length); | |
| 90 } | |
| 91 | |
| 92 MockCompiler compiler = new MockCompiler(); | |
| 93 ResolverVisitor visitor = compiler.resolverVisitor(); | |
| 94 compiler.parseScript('class Foo<T, U> {}'); | |
| 95 ClassElement foo = compiler.mainApp.find(buildSourceString('Foo')); | |
| 96 matchResolvedTypes(visitor, 'Foo<int, String> x;', 'Foo', | |
| 97 [compiler.intClass, compiler.stringClass]); | |
| 98 matchResolvedTypes(visitor, 'Foo<Foo, Foo> x;', 'Foo', | |
| 99 [foo, foo]); | |
| 100 | |
| 101 compiler = new MockCompiler(); | |
| 102 compiler.parseScript('class Foo<T, U> {}'); | |
| 103 compiler.resolveStatement('Foo<notype, int> x;'); | |
| 104 Expect.equals(1, compiler.warnings.length); | |
| 105 Expect.equals(MessageKind.CANNOT_RESOLVE_TYPE, | |
| 106 compiler.warnings[0].message.kind); | |
| 107 Expect.equals(0, compiler.errors.length); | |
| 108 | |
| 109 compiler = new MockCompiler(); | |
| 110 compiler.parseScript('class Foo<T, U> {}'); | |
| 111 compiler.resolveStatement('var x = new Foo<notype, int>();'); | |
| 112 Expect.equals(0, compiler.warnings.length); | |
| 113 Expect.equals(1, compiler.errors.length); | |
| 114 Expect.equals(MessageKind.CANNOT_RESOLVE_TYPE, | |
| 115 compiler.errors[0].message.kind); | |
| 116 } | |
| 117 | |
| 118 testSuperCalls() { | |
| 119 MockCompiler compiler = new MockCompiler(); | |
| 120 String script = """class A { foo() {} } | |
| 121 class B extends A { foo() => super.foo(); }"""; | |
| 122 compiler.parseScript(script); | |
| 123 compiler.resolveStatement("B b;"); | |
| 124 | |
| 125 ClassElement classB = compiler.mainApp.find(buildSourceString("B")); | |
| 126 FunctionElement fooB = classB.lookupLocalMember(buildSourceString("foo")); | |
| 127 ClassElement classA = compiler.mainApp.find(buildSourceString("A")); | |
| 128 FunctionElement fooA = classA.lookupLocalMember(buildSourceString("foo")); | |
| 129 | |
| 130 ResolverVisitor visitor = new ResolverVisitor(compiler, fooB); | |
| 131 FunctionExpression node = fooB.parseNode(compiler); | |
| 132 visitor.visit(node.body); | |
| 133 Map mapping = map(visitor); | |
| 134 | |
| 135 Send superCall = node.body.asReturn().expression; | |
| 136 FunctionElement called = mapping[superCall]; | |
| 137 Expect.isTrue(called !== null); | |
| 138 Expect.equals(fooA, called); | |
| 139 } | |
| 140 | |
| 141 testThis() { | |
| 142 MockCompiler compiler = new MockCompiler(); | |
| 143 compiler.parseScript("class Foo { foo() { return this; } }"); | |
| 144 compiler.resolveStatement("Foo foo;"); | |
| 145 ClassElement fooElement = compiler.mainApp.find(buildSourceString("Foo")); | |
| 146 FunctionElement funElement = | |
| 147 fooElement.lookupLocalMember(buildSourceString("foo")); | |
| 148 ResolverVisitor visitor = new ResolverVisitor(compiler, funElement); | |
| 149 FunctionExpression function = funElement.parseNode(compiler); | |
| 150 visitor.visit(function.body); | |
| 151 Map mapping = map(visitor); | |
| 152 List<Element> values = mapping.getValues(); | |
| 153 Expect.equals(0, mapping.length); | |
| 154 Expect.equals(0, compiler.warnings.length); | |
| 155 | |
| 156 compiler = new MockCompiler(); | |
| 157 compiler.resolveStatement("main() { return this; }"); | |
| 158 Expect.equals(0, compiler.warnings.length); | |
| 159 Expect.equals(1, compiler.errors.length); | |
| 160 Expect.equals(MessageKind.NO_INSTANCE_AVAILABLE, | |
| 161 compiler.errors[0].message.kind); | |
| 162 | |
| 163 compiler = new MockCompiler(); | |
| 164 compiler.parseScript("class Foo { static foo() { return this; } }"); | |
| 165 compiler.resolveStatement("Foo foo;"); | |
| 166 fooElement = compiler.mainApp.find(buildSourceString("Foo")); | |
| 167 funElement = | |
| 168 fooElement.lookupLocalMember(buildSourceString("foo")); | |
| 169 visitor = new ResolverVisitor(compiler, funElement); | |
| 170 function = funElement.parseNode(compiler); | |
| 171 visitor.visit(function.body); | |
| 172 Expect.equals(0, compiler.warnings.length); | |
| 173 Expect.equals(1, compiler.errors.length); | |
| 174 Expect.equals(MessageKind.NO_INSTANCE_AVAILABLE, | |
| 175 compiler.errors[0].message.kind); | |
| 176 } | |
| 177 | |
| 178 testLocalsOne() { | |
| 179 testLocals([["foo", false]]); | |
| 180 testLocals([["foo", false], ["bar", false]]); | |
| 181 testLocals([["foo", false], ["bar", false], ["foobar", false]]); | |
| 182 | |
| 183 testLocals([["foo", true]]); | |
| 184 testLocals([["foo", false], ["bar", true]]); | |
| 185 testLocals([["foo", true], ["bar", true]]); | |
| 186 | |
| 187 testLocals([["foo", false], ["bar", false], ["foobar", true]]); | |
| 188 testLocals([["foo", false], ["bar", true], ["foobar", true]]); | |
| 189 testLocals([["foo", true], ["bar", true], ["foobar", true]]); | |
| 190 | |
| 191 MockCompiler compiler = testLocals([["foo", false], ["foo", false]]); | |
| 192 Expect.equals(1, compiler.errors.length); | |
| 193 Expect.equals( | |
| 194 new Message(MessageKind.DUPLICATE_DEFINITION, ['foo']), | |
| 195 compiler.errors[0].message); | |
| 196 } | |
| 197 | |
| 198 | |
| 199 testLocalsTwo() { | |
| 200 MockCompiler compiler = new MockCompiler(); | |
| 201 ResolverVisitor visitor = compiler.resolverVisitor(); | |
| 202 Node tree = parseStatement("if (true) { var a = 1; var b = 2; }"); | |
| 203 Element element = visitor.visit(tree); | |
| 204 Expect.equals(null, element); | |
| 205 BlockScope scope = visitor.context; | |
| 206 Expect.equals(0, scope.elements.length); | |
| 207 Expect.equals(2, map(visitor).length); | |
| 208 | |
| 209 List<Element> elements = map(visitor).getValues(); | |
| 210 Expect.notEquals(elements[0], elements[1]); | |
| 211 } | |
| 212 | |
| 213 testLocalsThree() { | |
| 214 MockCompiler compiler = new MockCompiler(); | |
| 215 ResolverVisitor visitor = compiler.resolverVisitor(); | |
| 216 Node tree = parseStatement("{ var a = 1; if (true) { a; } }"); | |
| 217 Element element = visitor.visit(tree); | |
| 218 Expect.equals(null, element); | |
| 219 BlockScope scope = visitor.context; | |
| 220 Expect.equals(0, scope.elements.length); | |
| 221 Expect.equals(3, map(visitor).length); | |
| 222 List<Element> elements = map(visitor).getValues(); | |
| 223 Expect.equals(elements[0], elements[1]); | |
| 224 } | |
| 225 | |
| 226 testLocalsFour() { | |
| 227 MockCompiler compiler = new MockCompiler(); | |
| 228 ResolverVisitor visitor = compiler.resolverVisitor(); | |
| 229 Node tree = parseStatement("{ var a = 1; if (true) { var a = 1; } }"); | |
| 230 Element element = visitor.visit(tree); | |
| 231 Expect.equals(null, element); | |
| 232 BlockScope scope = visitor.context; | |
| 233 Expect.equals(0, scope.elements.length); | |
| 234 Expect.equals(2, map(visitor).length); | |
| 235 List<Element> elements = map(visitor).getValues(); | |
| 236 Expect.notEquals(elements[0], elements[1]); | |
| 237 } | |
| 238 | |
| 239 testLocalsFive() { | |
| 240 MockCompiler compiler = new MockCompiler(); | |
| 241 ResolverVisitor visitor = compiler.resolverVisitor(); | |
| 242 If tree = parseStatement("if (true) { var a = 1; a; } else { var a = 2; a;}"); | |
| 243 Element element = visitor.visit(tree); | |
| 244 Expect.equals(null, element); | |
| 245 BlockScope scope = visitor.context; | |
| 246 Expect.equals(0, scope.elements.length); | |
| 247 Expect.equals(6, map(visitor).length); | |
| 248 | |
| 249 Block thenPart = tree.thenPart; | |
| 250 List statements1 = thenPart.statements.nodes.toList(); | |
| 251 Node def1 = statements1[0].definitions.nodes.head; | |
| 252 Node id1 = statements1[1].expression; | |
| 253 Expect.equals(visitor.mapping[def1], visitor.mapping[id1]); | |
| 254 | |
| 255 Block elsePart = tree.elsePart; | |
| 256 List statements2 = elsePart.statements.nodes.toList(); | |
| 257 Node def2 = statements2[0].definitions.nodes.head; | |
| 258 Node id2 = statements2[1].expression; | |
| 259 Expect.equals(visitor.mapping[def2], visitor.mapping[id2]); | |
| 260 | |
| 261 Expect.notEquals(visitor.mapping[def1], visitor.mapping[def2]); | |
| 262 Expect.notEquals(visitor.mapping[id1], visitor.mapping[id2]); | |
| 263 } | |
| 264 | |
| 265 testParametersOne() { | |
| 266 MockCompiler compiler = new MockCompiler(); | |
| 267 ResolverVisitor visitor = compiler.resolverVisitor(); | |
| 268 FunctionExpression tree = | |
| 269 parseFunction("void foo(int a) { return a; }", compiler); | |
| 270 visitor.visit(tree); | |
| 271 | |
| 272 // Check that an element has been created for the parameter. | |
| 273 VariableDefinitions vardef = tree.parameters.nodes.head; | |
| 274 Node param = vardef.definitions.nodes.head; | |
| 275 Expect.equals(ElementKind.PARAMETER, visitor.mapping[param].kind); | |
| 276 | |
| 277 // Check that 'a' in 'return a' is resolved to the parameter. | |
| 278 Block body = tree.body; | |
| 279 Return ret = body.statements.nodes.head; | |
| 280 Send use = ret.expression; | |
| 281 Expect.equals(ElementKind.PARAMETER, visitor.mapping[use].kind); | |
| 282 Expect.equals(visitor.mapping[param], visitor.mapping[use]); | |
| 283 } | |
| 284 | |
| 285 testFor() { | |
| 286 MockCompiler compiler = new MockCompiler(); | |
| 287 ResolverVisitor visitor = compiler.resolverVisitor(); | |
| 288 For tree = parseStatement("for (int i = 0; i < 10; i = i + 1) { i = 5; }"); | |
| 289 visitor.visit(tree); | |
| 290 | |
| 291 BlockScope scope = visitor.context; | |
| 292 Expect.equals(0, scope.elements.length); | |
| 293 Expect.equals(10, map(visitor).length); | |
| 294 | |
| 295 VariableDefinitions initializer = tree.initializer; | |
| 296 Node iNode = initializer.definitions.nodes.head; | |
| 297 Element iElement = visitor.mapping[iNode]; | |
| 298 | |
| 299 // Check that we have the expected nodes. This test relies on the mapping | |
| 300 // field to be a linked hash map (preserving insertion order). | |
| 301 Expect.isTrue(map(visitor) is LinkedHashMap); | |
| 302 List<Node> nodes = map(visitor).getKeys(); | |
| 303 List<Element> elements = map(visitor).getValues(); | |
| 304 | |
| 305 | |
| 306 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 307 // ^^^ | |
| 308 Expect.isTrue(nodes[0] is TypeAnnotation); | |
| 309 | |
| 310 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 311 // ^^^^^ | |
| 312 checkSendSet(iElement, nodes[1], elements[1]); | |
| 313 | |
| 314 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 315 // ^ | |
| 316 checkIdentifier(iElement, nodes[2], elements[2]); | |
| 317 | |
| 318 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 319 // ^ | |
| 320 checkSend(iElement, nodes[3], elements[3]); | |
| 321 | |
| 322 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 323 // ^ | |
| 324 checkIdentifier(iElement, nodes[4], elements[4]); | |
| 325 | |
| 326 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 327 // ^ | |
| 328 checkIdentifier(iElement, nodes[5], elements[5]); | |
| 329 | |
| 330 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 331 // ^ | |
| 332 checkSend(iElement, nodes[6], elements[6]); | |
| 333 | |
| 334 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 335 // ^^^^^^^^^ | |
| 336 checkSendSet(iElement, nodes[7], elements[7]); | |
| 337 | |
| 338 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 339 // ^ | |
| 340 checkIdentifier(iElement, nodes[8], elements[8]); | |
| 341 | |
| 342 // for (int i = 0; i < 10; i = i + 1) { i = 5; }; | |
| 343 // ^^^^^ | |
| 344 checkSendSet(iElement, nodes[9], elements[9]); | |
| 345 } | |
| 346 | |
| 347 checkIdentifier(Element expected, Node node, Element actual) { | |
| 348 Expect.isTrue(node is Identifier, node.toDebugString()); | |
| 349 Expect.equals(expected, actual); | |
| 350 } | |
| 351 | |
| 352 checkSend(Element expected, Node node, Element actual) { | |
| 353 Expect.isTrue(node is Send, node.toDebugString()); | |
| 354 Expect.isTrue(node is !SendSet, node.toDebugString()); | |
| 355 Expect.equals(expected, actual); | |
| 356 } | |
| 357 | |
| 358 checkSendSet(Element expected, Node node, Element actual) { | |
| 359 Expect.isTrue(node is SendSet, node.toDebugString()); | |
| 360 Expect.equals(expected, actual); | |
| 361 } | |
| 362 | |
| 363 testTypeAnnotation() { | |
| 364 MockCompiler compiler = new MockCompiler(); | |
| 365 String statement = "Foo bar;"; | |
| 366 | |
| 367 // Test that we get a warning when Foo is not defined. | |
| 368 Map mapping = compiler.resolveStatement(statement).map; | |
| 369 | |
| 370 Expect.equals(1, mapping.length); // bar has an element. | |
| 371 Expect.equals(1, compiler.warnings.length); | |
| 372 | |
| 373 Node warningNode = compiler.warnings[0].node; | |
| 374 | |
| 375 Expect.equals( | |
| 376 new Message(MessageKind.CANNOT_RESOLVE_TYPE, ['Foo']), | |
| 377 compiler.warnings[0].message); | |
| 378 VariableDefinitions definition = compiler.parsedTree; | |
| 379 Expect.equals(warningNode, definition.type); | |
| 380 compiler.clearWarnings(); | |
| 381 | |
| 382 // Test that there is no warning after defining Foo. | |
| 383 compiler.parseScript("class Foo {}"); | |
| 384 mapping = compiler.resolveStatement(statement).map; | |
| 385 Expect.equals(2, mapping.length); | |
| 386 Expect.equals(0, compiler.warnings.length); | |
| 387 | |
| 388 // Test that 'var' does not create a warning. | |
| 389 mapping = compiler.resolveStatement("var foo;").map; | |
| 390 Expect.equals(1, mapping.length); | |
| 391 Expect.equals(0, compiler.warnings.length); | |
| 392 } | |
| 393 | |
| 394 testSuperclass() { | |
| 395 MockCompiler compiler = new MockCompiler(); | |
| 396 compiler.parseScript("class Foo extends Bar {}"); | |
| 397 compiler.resolveStatement("Foo bar;"); | |
| 398 Expect.equals(1, compiler.errors.length); | |
| 399 Expect.equals( | |
| 400 new Message(MessageKind.CANNOT_RESOLVE_TYPE, ['Bar']), | |
| 401 compiler.errors[0].message); | |
| 402 compiler.clearErrors(); | |
| 403 | |
| 404 compiler = new MockCompiler(); | |
| 405 compiler.parseScript("class Foo extends Bar {}"); | |
| 406 compiler.parseScript("class Bar {}"); | |
| 407 Map mapping = compiler.resolveStatement("Foo bar;").map; | |
| 408 Expect.equals(2, mapping.length); | |
| 409 | |
| 410 ClassElement fooElement = compiler.mainApp.find(buildSourceString('Foo')); | |
| 411 ClassElement barElement = compiler.mainApp.find(buildSourceString('Bar')); | |
| 412 Expect.equals(barElement.computeType(compiler), | |
| 413 fooElement.supertype); | |
| 414 Expect.isTrue(fooElement.interfaces.isEmpty()); | |
| 415 Expect.isTrue(barElement.interfaces.isEmpty()); | |
| 416 } | |
| 417 | |
| 418 testVarSuperclass() { | |
| 419 MockCompiler compiler = new MockCompiler(); | |
| 420 compiler.parseScript("class Foo extends var {}"); | |
| 421 compiler.resolveStatement("Foo bar;"); | |
| 422 Expect.equals(1, compiler.errors.length); | |
| 423 Expect.equals( | |
| 424 new Message(MessageKind.CANNOT_RESOLVE_TYPE, ['var']), | |
| 425 compiler.errors[0].message); | |
| 426 compiler.clearErrors(); | |
| 427 } | |
| 428 | |
| 429 testOneInterface() { | |
| 430 MockCompiler compiler = new MockCompiler(); | |
| 431 compiler.parseScript("class Foo implements Bar {}"); | |
| 432 compiler.resolveStatement("Foo bar;"); | |
| 433 Expect.equals(1, compiler.errors.length); | |
| 434 Expect.equals( | |
| 435 new Message(MessageKind.CANNOT_RESOLVE_TYPE, ['bar']), | |
| 436 compiler.errors[0].message); | |
| 437 compiler.clearErrors(); | |
| 438 | |
| 439 // Add the interface to the world and make sure everything is setup correctly. | |
| 440 compiler.parseScript("interface Bar {}"); | |
| 441 | |
| 442 ResolverVisitor visitor = new ResolverVisitor(compiler, null); | |
| 443 compiler.resolveStatement("Foo bar;"); | |
| 444 | |
| 445 ClassElement fooElement = compiler.mainApp.find(buildSourceString('Foo')); | |
| 446 ClassElement barElement = compiler.mainApp.find(buildSourceString('Bar')); | |
| 447 | |
| 448 Expect.equals(null, barElement.supertype); | |
| 449 Expect.isTrue(barElement.interfaces.isEmpty()); | |
| 450 | |
| 451 Expect.equals(barElement.computeType(compiler), | |
| 452 fooElement.interfaces.head); | |
| 453 Expect.equals(1, length(fooElement.interfaces)); | |
| 454 } | |
| 455 | |
| 456 testTwoInterfaces() { | |
| 457 MockCompiler compiler = new MockCompiler(); | |
| 458 compiler.parseScript( | |
| 459 "interface I1 {} interface I2 {} class C implements I1, I2 {}"); | |
| 460 compiler.resolveStatement("Foo bar;"); | |
| 461 | |
| 462 ClassElement c = compiler.mainApp.find(buildSourceString('C')); | |
| 463 Element i1 = compiler.mainApp.find(buildSourceString('I1')); | |
| 464 Element i2 = compiler.mainApp.find(buildSourceString('I2')); | |
| 465 | |
| 466 Expect.equals(2, length(c.interfaces)); | |
| 467 Expect.equals(i1.computeType(compiler), at(c.interfaces, 0)); | |
| 468 Expect.equals(i2.computeType(compiler), at(c.interfaces, 1)); | |
| 469 } | |
| 470 | |
| 471 testFunctionExpression() { | |
| 472 MockCompiler compiler = new MockCompiler(); | |
| 473 ResolverVisitor visitor = compiler.resolverVisitor(); | |
| 474 Map mapping = compiler.resolveStatement("int f() {}").map; | |
| 475 Expect.equals(3, mapping.length); | |
| 476 Element element; | |
| 477 Node node; | |
| 478 mapping.forEach((Node n, Element e) { | |
| 479 if (n is FunctionExpression) { | |
| 480 element = e; | |
| 481 node = n; | |
| 482 } | |
| 483 }); | |
| 484 Expect.equals(ElementKind.FUNCTION, element.kind); | |
| 485 Expect.equals(buildSourceString('f'), element.name); | |
| 486 Expect.equals(element.parseNode(compiler), node); | |
| 487 } | |
| 488 | |
| 489 testNewExpression() { | |
| 490 MockCompiler compiler = new MockCompiler(); | |
| 491 compiler.parseScript("class A {} foo() { print(new A()); }"); | |
| 492 ClassElement aElement = compiler.mainApp.find(buildSourceString('A')); | |
| 493 FunctionElement fooElement = compiler.mainApp.find(buildSourceString('foo')); | |
| 494 Expect.isTrue(aElement !== null); | |
| 495 Expect.isTrue(fooElement !== null); | |
| 496 | |
| 497 fooElement.parseNode(compiler); | |
| 498 compiler.resolver.resolve(fooElement); | |
| 499 | |
| 500 TreeElements elements = compiler.resolveStatement("new A();"); | |
| 501 NewExpression expression = | |
| 502 compiler.parsedTree.asExpressionStatement().expression; | |
| 503 Element element = elements[expression.send]; | |
| 504 Expect.equals(ElementKind.GENERATIVE_CONSTRUCTOR, element.kind); | |
| 505 Expect.isTrue(element is SynthesizedConstructorElement); | |
| 506 } | |
| 507 | |
| 508 testTopLevelFields() { | |
| 509 MockCompiler compiler = new MockCompiler(); | |
| 510 compiler.parseScript("int a;"); | |
| 511 VariableElement element = compiler.mainApp.find(buildSourceString("a")); | |
| 512 Expect.equals(ElementKind.FIELD, element.kind); | |
| 513 VariableDefinitions node = element.variables.parseNode(compiler); | |
| 514 Identifier typeName = node.type.typeName; | |
| 515 Expect.equals(typeName.source.slowToString(), 'int'); | |
| 516 | |
| 517 compiler.parseScript("var b, c;"); | |
| 518 VariableElement bElement = compiler.mainApp.find(buildSourceString("b")); | |
| 519 VariableElement cElement = compiler.mainApp.find(buildSourceString("c")); | |
| 520 Expect.equals(ElementKind.FIELD, bElement.kind); | |
| 521 Expect.equals(ElementKind.FIELD, cElement.kind); | |
| 522 Expect.isTrue(bElement != cElement); | |
| 523 | |
| 524 VariableDefinitions bNode = bElement.variables.parseNode(compiler); | |
| 525 VariableDefinitions cNode = cElement.variables.parseNode(compiler); | |
| 526 Expect.equals(bNode, cNode); | |
| 527 Expect.isNull(bNode.type); | |
| 528 Expect.isTrue(bNode.modifiers.isVar()); | |
| 529 } | |
| 530 | |
| 531 resolveConstructor(String script, String statement, String className, | |
| 532 String constructor, int expectedElementCount, | |
| 533 [List expectedWarnings = const [], | |
| 534 List expectedErrors = const [], | |
| 535 String corelib = DEFAULT_CORELIB]) { | |
| 536 MockCompiler compiler = new MockCompiler(corelib); | |
| 537 compiler.parseScript(script); | |
| 538 compiler.resolveStatement(statement); | |
| 539 ClassElement classElement = | |
| 540 compiler.mainApp.find(buildSourceString(className)); | |
| 541 Element element = | |
| 542 classElement.lookupConstructor(buildSourceString(constructor)); | |
| 543 FunctionExpression tree = element.parseNode(compiler); | |
| 544 ResolverVisitor visitor = new ResolverVisitor(compiler, element); | |
| 545 new InitializerResolver(visitor).resolveInitializers(element, tree); | |
| 546 visitor.visit(tree.body); | |
| 547 Expect.equals(expectedElementCount, map(visitor).length); | |
| 548 | |
| 549 compareWarningKinds(script, expectedWarnings, compiler.warnings); | |
| 550 compareWarningKinds(script, expectedErrors, compiler.errors); | |
| 551 } | |
| 552 | |
| 553 testClassHierarchy() { | |
| 554 final MAIN = buildSourceString("main"); | |
| 555 MockCompiler compiler = new MockCompiler(); | |
| 556 compiler.parseScript("""class A extends B {} | |
| 557 class B extends A {} | |
| 558 main() { return new A(); }"""); | |
| 559 FunctionElement mainElement = compiler.mainApp.find(MAIN); | |
| 560 compiler.resolver.resolve(mainElement); | |
| 561 Expect.equals(0, compiler.warnings.length); | |
| 562 Expect.equals(1, compiler.errors.length); | |
| 563 Expect.equals(MessageKind.CYCLIC_CLASS_HIERARCHY, | |
| 564 compiler.errors[0].message.kind); | |
| 565 | |
| 566 compiler = new MockCompiler(); | |
| 567 compiler.parseScript("""interface A extends B {} | |
| 568 interface B extends A {} | |
| 569 class C implements A {} | |
| 570 main() { return new C(); }"""); | |
| 571 mainElement = compiler.mainApp.find(MAIN); | |
| 572 compiler.resolver.resolve(mainElement); | |
| 573 Expect.equals(0, compiler.warnings.length); | |
| 574 Expect.equals(1, compiler.errors.length); | |
| 575 Expect.equals(MessageKind.CYCLIC_CLASS_HIERARCHY, | |
| 576 compiler.errors[0].message.kind); | |
| 577 | |
| 578 compiler = new MockCompiler(); | |
| 579 compiler.parseScript("""class A extends B {} | |
| 580 class B extends C {} | |
| 581 class C {} | |
| 582 main() { return new A(); }"""); | |
| 583 mainElement = compiler.mainApp.find(MAIN); | |
| 584 compiler.resolver.resolve(mainElement); | |
| 585 Expect.equals(0, compiler.warnings.length); | |
| 586 Expect.equals(0, compiler.errors.length); | |
| 587 ClassElement aElement = compiler.mainApp.find(buildSourceString("A")); | |
| 588 Link<Type> supertypes = aElement.allSupertypes; | |
| 589 Expect.equals(<String>['B', 'C', 'Object'].toString(), | |
| 590 asSortedStrings(supertypes).toString()); | |
| 591 } | |
| 592 | |
| 593 testInitializers() { | |
| 594 String script; | |
| 595 script = """class A { | |
| 596 int foo; int bar; | |
| 597 A() : this.foo = 1, bar = 2; | |
| 598 }"""; | |
| 599 resolveConstructor(script, "A a = new A();", "A", "A", 2); | |
| 600 | |
| 601 script = """class A { | |
| 602 int foo; A a; | |
| 603 A() : a.foo = 1; | |
| 604 }"""; | |
| 605 resolveConstructor(script, "A a = new A();", "A", "A", 0, | |
| 606 [], [MessageKind.INVALID_RECEIVER_IN_INITIALIZER]); | |
| 607 | |
| 608 script = """class A { | |
| 609 int foo; | |
| 610 A() : this.foo = 1, this.foo = 2; | |
| 611 }"""; | |
| 612 resolveConstructor(script, "A a = new A();", "A", "A", 2, | |
| 613 [MessageKind.ALREADY_INITIALIZED], | |
| 614 [MessageKind.DUPLICATE_INITIALIZER]); | |
| 615 | |
| 616 script = """class A { | |
| 617 A() : this.foo = 1; | |
| 618 }"""; | |
| 619 resolveConstructor(script, "A a = new A();", "A", "A", 0, | |
| 620 [], [MessageKind.CANNOT_RESOLVE]); | |
| 621 | |
| 622 script = """class A { | |
| 623 int foo; | |
| 624 int bar; | |
| 625 A() : this.foo = bar; | |
| 626 }"""; | |
| 627 resolveConstructor(script, "A a = new A();", "A", "A", 3, | |
| 628 [], [MessageKind.NO_INSTANCE_AVAILABLE]); | |
| 629 | |
| 630 script = """class A { | |
| 631 int foo() => 42; | |
| 632 A() : foo(); | |
| 633 }"""; | |
| 634 resolveConstructor(script, "A a = new A();", "A", "A", 0, | |
| 635 [], [MessageKind.CONSTRUCTOR_CALL_EXPECTED]); | |
| 636 | |
| 637 script = """class A { | |
| 638 int i; | |
| 639 A.a() : this.b(0); | |
| 640 A.b(int i); | |
| 641 }"""; | |
| 642 resolveConstructor(script, "A a = new A.a();", "A", @"A$a", 1, | |
| 643 [], []); | |
| 644 | |
| 645 script = """class A { | |
| 646 int i; | |
| 647 A.a() : i = 42, this(0); | |
| 648 A(int i); | |
| 649 }"""; | |
| 650 resolveConstructor(script, "A a = new A.a();", "A", @"A$a", 2, | |
| 651 [], [MessageKind.REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER]); | |
| 652 | |
| 653 script = """class A { | |
| 654 int i; | |
| 655 A(i); | |
| 656 } | |
| 657 class B extends A { | |
| 658 B() : super(0); | |
| 659 }"""; | |
| 660 resolveConstructor(script, "B a = new B();", "B", "B", 1, | |
| 661 [], []); | |
| 662 | |
| 663 script = """class A { | |
| 664 int i; | |
| 665 A(i); | |
| 666 } | |
| 667 class B extends A { | |
| 668 B() : super(0), super(1); | |
| 669 }"""; | |
| 670 resolveConstructor(script, "B b = new B();", "B", "B", 2, | |
| 671 [], [MessageKind.DUPLICATE_SUPER_INITIALIZER]); | |
| 672 | |
| 673 script = ""; | |
| 674 final String CORELIB_WITH_INVALID_OBJECT = | |
| 675 '''print(var obj) {} | |
| 676 class int {} | |
| 677 class double {} | |
| 678 class bool {} | |
| 679 class String {} | |
| 680 class num {} | |
| 681 class Function {} | |
| 682 class List {} | |
| 683 class Closure {} | |
| 684 class Null {} | |
| 685 class Dynamic {} | |
| 686 class Object { Object() : super(); }'''; | |
| 687 resolveConstructor(script, "Object o = new Object();", "Object", "Object", 1, | |
| 688 [], [MessageKind.SUPER_INITIALIZER_IN_OBJECT], | |
| 689 corelib: CORELIB_WITH_INVALID_OBJECT); | |
| 690 } | |
| 691 | |
| 692 map(ResolverVisitor visitor) { | |
| 693 TreeElementMapping elements = visitor.mapping; | |
| 694 return elements.map; | |
| 695 } | |
| 696 | |
| 697 length(Link link) => link.isEmpty() ? 0 : length(link.tail) + 1; | |
| 698 | |
| 699 at(Link link, int index) => (index == 0) ? link.head : at(link.tail, index - 1); | |
| 700 | |
| 701 List<String> asSortedStrings(Link link) { | |
| 702 List<String> result = <String>[]; | |
| 703 for (; !link.isEmpty(); link = link.tail) result.add(link.head.toString()); | |
| 704 result.sort((s1, s2) => s1.compareTo(s2)); | |
| 705 return result; | |
| 706 } | |
| OLD | NEW |