| OLD | NEW |
| (Empty) | |
| 1 #import("dart:uri"); |
| 2 #import("../../../lib/compiler/implementation/elements/elements.dart"); |
| 3 #import('../../../lib/compiler/implementation/scanner/scannerlib.dart'); |
| 4 #import('../../../lib/compiler/implementation/source_file.dart'); |
| 5 #import('../../../lib/compiler/implementation/types/types.dart'); |
| 6 #import('../../../lib/compiler/implementation/tree/tree.dart'); |
| 7 #import("../../../lib/compiler/implementation/leg.dart", prefix: "leg"); |
| 8 |
| 9 #import("parser_helper.dart"); |
| 10 #import("compiler_helper.dart"); |
| 11 #import("mock_compiler.dart"); |
| 12 |
| 13 /** |
| 14 * Finds the node corresponding to the last occurence of the substring |
| 15 * [: identifier; :] in the program represented by the visited AST. |
| 16 */ |
| 17 class VariableFinderVisitor extends Visitor { |
| 18 final String identifier; |
| 19 Node result; |
| 20 |
| 21 VariableFinderVisitor(this.identifier); |
| 22 |
| 23 visitSend(Send node) { |
| 24 if (node.isPropertyAccess |
| 25 && node.selector.asIdentifier().source.slowToString() == identifier) { |
| 26 result = node; |
| 27 } else { |
| 28 node.visitChildren(this); |
| 29 } |
| 30 } |
| 31 |
| 32 visitNode(Node node) { |
| 33 node.visitChildren(this); |
| 34 } |
| 35 } |
| 36 |
| 37 class AnalysisResult { |
| 38 MockCompiler compiler; |
| 39 ConcreteTypesInferrer inferrer; |
| 40 Node ast; |
| 41 |
| 42 BaseType int; |
| 43 BaseType double; |
| 44 BaseType bool; |
| 45 BaseType string; |
| 46 |
| 47 AnalysisResult(MockCompiler compiler, ConcreteTypesInferrer inferrer) |
| 48 : this.compiler = compiler, |
| 49 this.inferrer = inferrer, |
| 50 int = inferrer.baseTypes.intBaseType, |
| 51 double = inferrer.baseTypes.doubleBaseType, |
| 52 bool = inferrer.baseTypes.boolBaseType, |
| 53 string = inferrer.baseTypes.stringBaseType { |
| 54 Element mainElement = compiler.mainApp.find(buildSourceString('main')); |
| 55 ast = mainElement.parseNode(compiler); |
| 56 } |
| 57 |
| 58 BaseType base(String className) { |
| 59 final source = buildSourceString(className); |
| 60 return new ClassBaseType(compiler.mainApp.find(source)); |
| 61 } |
| 62 |
| 63 /** |
| 64 * Finds the [Node] corresponding to the last occurence of the substring |
| 65 * [: identifier; :] in the program represented by the visited AST. For |
| 66 * instance, returns the AST node representing [: foo; :] in |
| 67 * [: main() { foo = 1; foo; } :]. |
| 68 */ |
| 69 Node findNode(String identifier) { |
| 70 VariableFinderVisitor finder = new VariableFinderVisitor(identifier); |
| 71 ast.accept(finder); |
| 72 return finder.result; |
| 73 } |
| 74 |
| 75 /** |
| 76 * Finds the [Element] corresponding to [: className#fieldName :]. |
| 77 */ |
| 78 Element findField(String className, String fieldName) { |
| 79 ClassElement element = compiler.mainApp.find(buildSourceString(className)); |
| 80 return element.lookupLocalMember(buildSourceString(fieldName)); |
| 81 } |
| 82 |
| 83 static ConcreteType concreteFrom(List<BaseType> baseTypes) { |
| 84 ConcreteType result = new ConcreteType.empty(); |
| 85 for (final baseType in baseTypes) { |
| 86 result = result.union(new ConcreteType.singleton(baseType)); |
| 87 } |
| 88 return result; |
| 89 } |
| 90 |
| 91 /** |
| 92 * Checks that the inferred type of the node corresponding to the last |
| 93 * occurence of [: variable; :] in the program is the concrete type |
| 94 * made of [baseTypes]. |
| 95 */ |
| 96 void checkNodeHasType(String variable, List<BaseType> baseTypes) { |
| 97 return Expect.equals( |
| 98 concreteFrom(baseTypes), |
| 99 inferrer.inferredTypes[findNode(variable)]); |
| 100 } |
| 101 |
| 102 /** |
| 103 * Checks that [: className#fieldName :]'s inferred type is the concrete type |
| 104 * made of [baseTypes]. |
| 105 */ |
| 106 void checkFieldHasType(String className, String fieldName, |
| 107 List<BaseType> baseTypes) { |
| 108 return Expect.equals( |
| 109 concreteFrom(baseTypes), |
| 110 inferrer.inferredFieldTypes[findField(className, fieldName)]); |
| 111 } |
| 112 } |
| 113 |
| 114 AnalysisResult analyze(String code) { |
| 115 Uri uri = new Uri.fromComponents(scheme: 'source'); |
| 116 MockCompiler compiler = new MockCompiler(); |
| 117 compiler.sourceFiles[uri.toString()] = new SourceFile(uri.toString(), code); |
| 118 compiler.runCompiler(uri); |
| 119 ConcreteTypesInferrer inferrer = new ConcreteTypesInferrer(compiler); |
| 120 inferrer.analyzeMain(compiler.mainApp.find(const SourceString("main"))); |
| 121 return new AnalysisResult(compiler, inferrer); |
| 122 } |
| 123 |
| 124 testLiterals() { |
| 125 final String source = r""" |
| 126 main() { |
| 127 var v1 = 42; |
| 128 var v2 = 42.0; |
| 129 var v3 = 'abc'; |
| 130 var v4 = true; |
| 131 var v5 = null; |
| 132 v1; v2; v3; v4; v5; |
| 133 } |
| 134 """; |
| 135 AnalysisResult result = analyze(source); |
| 136 result.checkNodeHasType('v1', [result.int]); |
| 137 result.checkNodeHasType('v2', [result.double]); |
| 138 result.checkNodeHasType('v3', [result.string]); |
| 139 result.checkNodeHasType('v4', [result.bool]); |
| 140 result.checkNodeHasType('v5', [new NullBaseType()]); |
| 141 } |
| 142 |
| 143 testRedefinition() { |
| 144 final String source = r""" |
| 145 main() { |
| 146 var foo = 42; |
| 147 foo = 'abc'; |
| 148 foo; |
| 149 } |
| 150 """; |
| 151 AnalysisResult result = analyze(source); |
| 152 result.checkNodeHasType('foo', [result.string]); |
| 153 } |
| 154 |
| 155 testIfThenElse() { |
| 156 final String source = r""" |
| 157 main() { |
| 158 var foo = 42; |
| 159 if (true) { |
| 160 foo = 'abc'; |
| 161 } else { |
| 162 foo = false; |
| 163 } |
| 164 foo; |
| 165 } |
| 166 """; |
| 167 AnalysisResult result = analyze(source); |
| 168 result.checkNodeHasType('foo', [result.string, result.bool]); |
| 169 } |
| 170 |
| 171 testTernaryIf() { |
| 172 final String source = r""" |
| 173 main() { |
| 174 var foo = 42; |
| 175 foo = true ? 'abc' : false; |
| 176 foo; |
| 177 } |
| 178 """; |
| 179 AnalysisResult result = analyze(source); |
| 180 result.checkNodeHasType('foo', [result.string, result.bool]); |
| 181 } |
| 182 |
| 183 testWhile() { |
| 184 final String source = r""" |
| 185 class A { f() => new B(); } |
| 186 class B { f() => new C(); } |
| 187 class C { f() => new A(); } |
| 188 main() { |
| 189 var foo = new A(); |
| 190 while(true) { |
| 191 foo = foo.f(); |
| 192 } |
| 193 foo; |
| 194 } |
| 195 """; |
| 196 AnalysisResult result = analyze(source); |
| 197 result.checkNodeHasType( |
| 198 'foo', |
| 199 [result.base('A'), result.base('B'), result.base('C')]); |
| 200 } |
| 201 |
| 202 testNonRecusiveFunction() { |
| 203 final String source = r""" |
| 204 f(x, y) => true ? x : y; |
| 205 main() { var foo = f(42, "abc"); foo; } |
| 206 """; |
| 207 AnalysisResult result = analyze(source); |
| 208 result.checkNodeHasType('foo', [result.int, result.string]); |
| 209 } |
| 210 |
| 211 testRecusiveFunction() { |
| 212 final String source = r""" |
| 213 f(x) { |
| 214 if (true) return x; |
| 215 else return f(true ? x : "abc"); |
| 216 } |
| 217 main() { var foo = f(42); foo; } |
| 218 """; |
| 219 AnalysisResult result = analyze(source); |
| 220 result.checkNodeHasType('foo', [result.int, result.string]); |
| 221 } |
| 222 |
| 223 testMutuallyRecusiveFunction() { |
| 224 final String source = r""" |
| 225 f() => true ? 42 : g(); |
| 226 g() => true ? "abc" : f(); |
| 227 main() { var foo = f(); foo; } |
| 228 """; |
| 229 AnalysisResult result = analyze(source); |
| 230 result.checkNodeHasType('foo', [result.int, result.string]); |
| 231 } |
| 232 |
| 233 testConstructor() { |
| 234 final String source = r""" |
| 235 class A { |
| 236 var x, y, z; |
| 237 A(this.x, a) : y = a { z = 'abc'; } |
| 238 } |
| 239 main() { |
| 240 new A(42, 'abc'); |
| 241 new A(true, null); |
| 242 } |
| 243 """; |
| 244 AnalysisResult result = analyze(source); |
| 245 result.checkFieldHasType('A', 'x', [result.int, result.bool]); |
| 246 result.checkFieldHasType('A', 'y', [result.string, new NullBaseType()]); |
| 247 result.checkFieldHasType('A', 'z', [result.string]); |
| 248 } |
| 249 |
| 250 testGetters() { |
| 251 final String source = r""" |
| 252 class A { |
| 253 var x; |
| 254 A(this.x); |
| 255 get y() => x; |
| 256 } |
| 257 main() { |
| 258 var a = new A(42); |
| 259 var foo = a.x; |
| 260 var bar = a.y; |
| 261 foo; bar; |
| 262 } |
| 263 """; |
| 264 AnalysisResult result = analyze(source); |
| 265 result.checkNodeHasType('foo', [result.int]); |
| 266 result.checkNodeHasType('bar', [result.int]); |
| 267 } |
| 268 |
| 269 testSetters() { |
| 270 final String source = r""" |
| 271 class A { |
| 272 var x; |
| 273 A(this.x); |
| 274 set y(a) { x = a; } |
| 275 } |
| 276 main() { |
| 277 var a = new A(42); |
| 278 a.x = 'abc'; |
| 279 a.y = true; |
| 280 } |
| 281 """; |
| 282 AnalysisResult result = analyze(source); |
| 283 result.checkFieldHasType('A', 'x', [result.int, result.string, result.bool]); |
| 284 } |
| 285 |
| 286 testNamedParameters() { |
| 287 final String source = r""" |
| 288 class A { |
| 289 var x, y, z, w; |
| 290 A(this.x, [this.y, this.z, this.w]); |
| 291 } |
| 292 main() { |
| 293 new A(42); |
| 294 new A('abc', w: true, z: 42.0); |
| 295 } |
| 296 """; |
| 297 AnalysisResult result = analyze(source); |
| 298 result.checkFieldHasType('A', 'x', [result.int, result.string]); |
| 299 result.checkFieldHasType('A', 'y', [new NullBaseType()]); |
| 300 result.checkFieldHasType('A', 'z', [new NullBaseType(), result.double]); |
| 301 result.checkFieldHasType('A', 'w', [new NullBaseType(), result.bool]); |
| 302 } |
| 303 |
| 304 void main() { |
| 305 testLiterals(); |
| 306 testRedefinition(); |
| 307 testIfThenElse(); |
| 308 testTernaryIf(); |
| 309 testWhile(); |
| 310 testNonRecusiveFunction(); |
| 311 testRecusiveFunction(); |
| 312 testMutuallyRecusiveFunction(); |
| 313 testConstructor(); |
| 314 testGetters(); |
| 315 testSetters(); |
| 316 testNamedParameters(); |
| 317 } |
| OLD | NEW |