OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 import 'package:kernel/ast.dart'; | 4 import 'package:kernel/ast.dart'; |
5 import 'package:kernel/text/ast_to_text.dart'; | 5 import 'package:kernel/text/ast_to_text.dart'; |
6 import 'package:kernel/verifier.dart'; | 6 import 'package:kernel/verifier.dart'; |
7 import 'package:test/test.dart'; | 7 import 'package:test/test.dart'; |
8 | 8 |
9 /// Checks that the verifier correctly find errors in invalid programs. | 9 /// Checks that the verifier correctly find errors in invalid programs. |
10 /// | 10 /// |
11 /// The frontend should never generate invalid programs, so we have to test | 11 /// The frontend should never generate invalid programs, so we have to test |
12 /// these by manually constructing invalid ASTs. | 12 /// these by manually constructing invalid ASTs. |
13 /// | 13 /// |
14 /// We mostly test negative cases here, as we get plenty of positive cases by | 14 /// We mostly test negative cases here, as we get plenty of positive cases by |
15 /// compiling the Dart test suite with the verifier enabled. | 15 /// compiling the Dart test suite with the verifier enabled. |
16 main() { | 16 main() { |
17 positiveTest('Test harness has no errors', () { | 17 positiveTest('Test harness has no errors', (TestHarness test) { |
18 return new NullLiteral(); | 18 return new NullLiteral(); |
19 }); | 19 }); |
20 negativeTest('VariableGet out of scope', () { | 20 negativeTest('VariableGet out of scope', (TestHarness test) { |
21 return new VariableGet(makeVariable()); | 21 return new VariableGet(test.makeVariable()); |
22 }); | 22 }); |
23 negativeTest('VariableSet out of scope', () { | 23 negativeTest('VariableSet out of scope', (TestHarness test) { |
24 return new VariableSet(makeVariable(), new NullLiteral()); | 24 return new VariableSet(test.makeVariable(), new NullLiteral()); |
25 }); | 25 }); |
26 negativeTest('Variable block scope', () { | 26 negativeTest('Variable block scope', (TestHarness test) { |
27 VariableDeclaration variable = makeVariable(); | 27 VariableDeclaration variable = test.makeVariable(); |
28 return new Block([ | 28 return new Block([ |
29 new Block([variable]), | 29 new Block([variable]), |
30 new ReturnStatement(new VariableGet(variable)) | 30 new ReturnStatement(new VariableGet(variable)) |
31 ]); | 31 ]); |
32 }); | 32 }); |
33 negativeTest('Variable let scope', () { | 33 negativeTest('Variable let scope', (TestHarness test) { |
34 VariableDeclaration variable = makeVariable(); | 34 VariableDeclaration variable = test.makeVariable(); |
35 return new LogicalExpression(new Let(variable, new VariableGet(variable)), | 35 return new LogicalExpression(new Let(variable, new VariableGet(variable)), |
36 '&&', new VariableGet(variable)); | 36 '&&', new VariableGet(variable)); |
37 }); | 37 }); |
38 negativeTest('Variable redeclared', () { | 38 negativeTest('Variable redeclared', (TestHarness test) { |
39 VariableDeclaration variable = makeVariable(); | 39 VariableDeclaration variable = test.makeVariable(); |
40 return new Block([variable, variable]); | 40 return new Block([variable, variable]); |
41 }); | 41 }); |
42 negativeTest('Member redeclared', () { | 42 negativeTest('Member redeclared', (TestHarness test) { |
43 Field field = new Field(new Name('field'), initializer: new NullLiteral()); | 43 Field field = new Field(new Name('field'), initializer: new NullLiteral()); |
44 return new Class( | 44 return new Class( |
45 name: 'Test', | 45 name: 'Test', |
46 supertype: objectClass.asRawSupertype, | 46 supertype: test.objectClass.asRawSupertype, |
47 fields: [field, field]); | 47 fields: [field, field]); |
48 }); | 48 }); |
49 negativeTest('Class redeclared', () { | 49 negativeTest('Class redeclared', (TestHarness test) { |
50 return otherClass; // Test harness also adds otherClass to program. | 50 return test.otherClass; // Test harness also adds otherClass to program. |
51 }); | 51 }); |
52 negativeTest('Class type parameter redeclared', () { | 52 negativeTest('Class type parameter redeclared', (TestHarness test) { |
53 var parameter = makeTypeParameter(); | 53 var parameter = test.makeTypeParameter(); |
54 return new Class( | 54 return new Class( |
55 name: 'Test', | 55 name: 'Test', |
56 supertype: objectClass.asRawSupertype, | 56 supertype: test.objectClass.asRawSupertype, |
57 typeParameters: [parameter, parameter]); | 57 typeParameters: [parameter, parameter]); |
58 }); | 58 }); |
59 negativeTest('Member type parameter redeclared', () { | 59 negativeTest('Member type parameter redeclared', (TestHarness test) { |
60 var parameter = makeTypeParameter(); | 60 var parameter = test.makeTypeParameter(); |
61 return new Procedure( | 61 return new Procedure( |
62 new Name('test'), | 62 new Name('bar'), |
63 ProcedureKind.Method, | 63 ProcedureKind.Method, |
64 new FunctionNode(new ReturnStatement(new NullLiteral()), | 64 new FunctionNode(new ReturnStatement(new NullLiteral()), |
65 typeParameters: [parameter, parameter])); | 65 typeParameters: [parameter, parameter])); |
66 }); | 66 }); |
67 negativeTest('Type parameter out of scope', () { | 67 negativeTest('Type parameter out of scope', (TestHarness test) { |
68 var parameter = makeTypeParameter(); | 68 var parameter = test.makeTypeParameter(); |
69 return new ListLiteral([], typeArgument: new TypeParameterType(parameter)); | 69 return new ListLiteral([], typeArgument: new TypeParameterType(parameter)); |
70 }); | 70 }); |
71 negativeTest('Class type parameter from another class', () { | 71 negativeTest('Class type parameter from another class', (TestHarness test) { |
72 return new TypeLiteral(new TypeParameterType(otherClass.typeParameters[0])); | 72 return new TypeLiteral( |
| 73 new TypeParameterType(test.otherClass.typeParameters[0])); |
73 }); | 74 }); |
74 negativeTest('Class type parameter in static method', () { | 75 negativeTest('Class type parameter in static method', (TestHarness test) { |
75 return new Procedure( | 76 return new Procedure( |
76 new Name('test'), | 77 new Name('bar'), |
77 ProcedureKind.Method, | 78 ProcedureKind.Method, |
78 new FunctionNode(new ReturnStatement( | 79 new FunctionNode(new ReturnStatement( |
79 new TypeLiteral(new TypeParameterType(classTypeParameter)))), | 80 new TypeLiteral(new TypeParameterType(test.classTypeParameter)))), |
80 isStatic: true); | 81 isStatic: true); |
81 }); | 82 }); |
82 negativeTest('Class type parameter in static field', () { | 83 negativeTest('Class type parameter in static field', (TestHarness test) { |
83 return new Field(new Name('field'), | 84 return new Field(new Name('field'), |
84 initializer: new TypeLiteral(new TypeParameterType(classTypeParameter)), | 85 initializer: |
| 86 new TypeLiteral(new TypeParameterType(test.classTypeParameter)), |
85 isStatic: true); | 87 isStatic: true); |
86 }); | 88 }); |
87 negativeTest('Method type parameter out of scope', () { | 89 negativeTest('Method type parameter out of scope', (TestHarness test) { |
88 var parameter = makeTypeParameter(); | 90 var parameter = test.makeTypeParameter(); |
89 return new Class( | 91 return new Class( |
90 name: 'Test', | 92 name: 'Test', |
91 supertype: objectClass.asRawSupertype, | 93 supertype: test.objectClass.asRawSupertype, |
92 procedures: [ | 94 procedures: [ |
93 new Procedure( | 95 new Procedure( |
94 new Name('generic'), | 96 new Name('generic'), |
95 ProcedureKind.Method, | 97 ProcedureKind.Method, |
96 new FunctionNode(new EmptyStatement(), | 98 new FunctionNode(new EmptyStatement(), |
97 typeParameters: [parameter])), | 99 typeParameters: [parameter])), |
98 new Procedure( | 100 new Procedure( |
99 new Name('use'), | 101 new Name('use'), |
100 ProcedureKind.Method, | 102 ProcedureKind.Method, |
101 new FunctionNode(new ReturnStatement( | 103 new FunctionNode(new ReturnStatement( |
102 new TypeLiteral(new TypeParameterType(parameter))))) | 104 new TypeLiteral(new TypeParameterType(parameter))))) |
103 ]); | 105 ]); |
104 }); | 106 }); |
105 negativeTest('Interface type arity too low', () { | 107 negativeTest('Interface type arity too low', (TestHarness test) { |
106 return new TypeLiteral(new InterfaceType(otherClass, [])); | 108 return new TypeLiteral(new InterfaceType(test.otherClass, [])); |
107 }); | 109 }); |
108 negativeTest('Interface type arity too high', () { | 110 negativeTest('Interface type arity too high', (TestHarness test) { |
109 return new TypeLiteral( | 111 return new TypeLiteral(new InterfaceType( |
110 new InterfaceType(otherClass, [new DynamicType(), new DynamicType()])); | 112 test.otherClass, [new DynamicType(), new DynamicType()])); |
111 }); | 113 }); |
112 negativeTest('Dangling interface type', () { | 114 negativeTest('Dangling interface type', (TestHarness test) { |
113 return new TypeLiteral(new InterfaceType(new Class())); | 115 var orphan = new Class(); |
| 116 return new TypeLiteral(new InterfaceType(orphan)); |
114 }); | 117 }); |
115 negativeTest('Dangling field get', () { | 118 negativeTest('Dangling field get', (TestHarness test) { |
116 return new DirectPropertyGet(new NullLiteral(), new Field(new Name('foo'))); | 119 var orphan = new Field(new Name('foo')); |
| 120 return new DirectPropertyGet(new NullLiteral(), orphan); |
117 }); | 121 }); |
118 negativeTest('Missing block parent pointer', () { | 122 negativeTest('Missing block parent pointer', (TestHarness test) { |
119 var block = new Block([]); | 123 var block = new Block([]); |
120 block.statements.add(new ReturnStatement()); | 124 block.statements.add(new ReturnStatement()); |
121 return block; | 125 return block; |
122 }); | 126 }); |
123 negativeTest('Missing function parent pointer', () { | 127 negativeTest('Missing function parent pointer', (TestHarness test) { |
124 var procedure = new Procedure(new Name('test'), ProcedureKind.Method, null); | 128 var procedure = new Procedure(new Name('bar'), ProcedureKind.Method, null); |
125 procedure.function = new FunctionNode(new EmptyStatement()); | 129 procedure.function = new FunctionNode(new EmptyStatement()); |
126 return procedure; | 130 return procedure; |
127 }); | 131 }); |
128 negativeTest('StaticGet without target', () { | 132 negativeTest('StaticGet without target', (TestHarness test) { |
129 return new StaticGet(null); | 133 return new StaticGet(null); |
130 }); | 134 }); |
131 negativeTest('StaticSet without target', () { | 135 negativeTest('StaticSet without target', (TestHarness test) { |
132 return new StaticSet(null, new NullLiteral()); | 136 return new StaticSet(null, new NullLiteral()); |
133 }); | 137 }); |
134 negativeTest('StaticInvocation without target', () { | 138 negativeTest('StaticInvocation without target', (TestHarness test) { |
135 return new StaticInvocation(null, new Arguments.empty()); | 139 return new StaticInvocation(null, new Arguments.empty()); |
136 }); | 140 }); |
137 positiveTest('Correct StaticInvocation', () { | 141 positiveTest('Correct StaticInvocation', (TestHarness test) { |
138 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 142 var method = new Procedure( |
| 143 new Name('foo'), |
| 144 ProcedureKind.Method, |
| 145 new FunctionNode(new EmptyStatement(), |
| 146 positionalParameters: [new VariableDeclaration('p')]), |
139 isStatic: true); | 147 isStatic: true); |
140 method.function = new FunctionNode( | 148 test.enclosingClass.addMember(method); |
141 new ReturnStatement( | 149 return new StaticInvocation(method, new Arguments([new NullLiteral()])); |
142 new StaticInvocation(method, new Arguments([new NullLiteral()]))), | |
143 positionalParameters: [new VariableDeclaration('p')])..parent = method; | |
144 return new Class( | |
145 name: 'Test', | |
146 supertype: objectClass.asRawSupertype, | |
147 procedures: [method]); | |
148 }); | 150 }); |
149 negativeTest('StaticInvocation with too many parameters', () { | 151 negativeTest('StaticInvocation with too many parameters', (TestHarness test) { |
150 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 152 var method = new Procedure(new Name('bar'), ProcedureKind.Method, |
| 153 new FunctionNode(new EmptyStatement()), |
151 isStatic: true); | 154 isStatic: true); |
152 method.function = new FunctionNode(new ReturnStatement( | 155 test.enclosingClass.addMember(method); |
153 new StaticInvocation(method, new Arguments([new NullLiteral()])))) | 156 return new StaticInvocation(method, new Arguments([new NullLiteral()])); |
154 ..parent = method; | |
155 return new Class( | |
156 name: 'Test', | |
157 supertype: objectClass.asRawSupertype, | |
158 procedures: [method]); | |
159 }); | 157 }); |
160 negativeTest('StaticInvocation with too few parameters', () { | 158 negativeTest('StaticInvocation with too few parameters', (TestHarness test) { |
161 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 159 var method = new Procedure( |
| 160 new Name('bar'), |
| 161 ProcedureKind.Method, |
| 162 new FunctionNode(new EmptyStatement(), |
| 163 positionalParameters: [new VariableDeclaration('p')]), |
162 isStatic: true); | 164 isStatic: true); |
163 method.function = new FunctionNode( | 165 test.enclosingClass.addMember(method); |
164 new ReturnStatement( | 166 return new StaticInvocation(method, new Arguments.empty()); |
165 new StaticInvocation(method, new Arguments.empty())), | |
166 positionalParameters: [new VariableDeclaration('p')])..parent = method; | |
167 return new Class( | |
168 name: 'Test', | |
169 supertype: objectClass.asRawSupertype, | |
170 procedures: [method]); | |
171 }); | 167 }); |
172 negativeTest('StaticInvocation with unmatched named parameter', () { | 168 negativeTest('StaticInvocation with unmatched named parameter', |
173 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 169 (TestHarness test) { |
| 170 var method = new Procedure(new Name('bar'), ProcedureKind.Method, |
| 171 new FunctionNode(new EmptyStatement()), |
174 isStatic: true); | 172 isStatic: true); |
175 method.function = new FunctionNode(new ReturnStatement(new StaticInvocation( | 173 test.enclosingClass.addMember(method); |
| 174 return new StaticInvocation( |
176 method, | 175 method, |
177 new Arguments([], | 176 new Arguments([], |
178 named: [new NamedExpression('p', new NullLiteral())])))) | 177 named: [new NamedExpression('p', new NullLiteral())])); |
179 ..parent = method; | 178 }); |
180 return new Class( | 179 negativeTest('StaticInvocation with missing type argument', |
| 180 (TestHarness test) { |
| 181 var method = new Procedure( |
| 182 new Name('bar'), |
| 183 ProcedureKind.Method, |
| 184 new FunctionNode(new EmptyStatement(), |
| 185 typeParameters: [test.makeTypeParameter()]), |
| 186 isStatic: true); |
| 187 test.enclosingClass.addMember(method); |
| 188 return new StaticInvocation(method, new Arguments.empty()); |
| 189 }); |
| 190 negativeTest('ConstructorInvocation with missing type argument', |
| 191 (TestHarness test) { |
| 192 var class_ = new Class( |
181 name: 'Test', | 193 name: 'Test', |
182 supertype: objectClass.asRawSupertype, | 194 typeParameters: [test.makeTypeParameter()], |
183 procedures: [method]); | 195 supertype: test.objectClass.asRawSupertype); |
184 }); | 196 test.enclosingLibrary.addClass(class_); |
185 negativeTest('StaticInvocation with missing type argument', () { | 197 var constructor = new Constructor(new FunctionNode(new EmptyStatement()), |
186 var method = new Procedure(new Name('test'), ProcedureKind.Method, null, | 198 name: new Name('foo')); |
187 isStatic: true); | 199 test.enclosingClass.addMember(constructor); |
188 method.function = new FunctionNode( | 200 return new ConstructorInvocation(constructor, new Arguments.empty()); |
189 new ReturnStatement( | |
190 new StaticInvocation(method, new Arguments.empty())), | |
191 typeParameters: [makeTypeParameter()])..parent = method; | |
192 return new Class( | |
193 name: 'Test', | |
194 supertype: objectClass.asRawSupertype, | |
195 procedures: [method]); | |
196 }); | |
197 negativeTest('ConstructorInvocation with missing type argument', () { | |
198 var constructor = new Constructor(null); | |
199 constructor.function = new FunctionNode(new ReturnStatement( | |
200 new ConstructorInvocation(constructor, new Arguments.empty()))) | |
201 ..parent = constructor; | |
202 return new Class( | |
203 name: 'Test', | |
204 typeParameters: [makeTypeParameter()], | |
205 supertype: objectClass.asRawSupertype, | |
206 constructors: [constructor]); | |
207 }); | 201 }); |
208 } | 202 } |
209 | 203 |
210 checkHasError(Program program) { | 204 checkHasError(Program program) { |
211 bool passed = false; | 205 bool passed = false; |
212 try { | 206 try { |
213 verifyProgram(program); | 207 verifyProgram(program); |
214 passed = true; | 208 passed = true; |
215 } catch (e) {} | 209 } catch (e) {} |
216 if (passed) { | 210 if (passed) { |
217 fail('Failed to reject invalid program:\n${programToString(program)}'); | 211 fail('Failed to reject invalid program:\n${programToString(program)}'); |
218 } | 212 } |
219 } | 213 } |
220 | 214 |
221 Class objectClass = new Class(name: 'Object'); | 215 class TestHarness { |
| 216 Program program; |
| 217 Class objectClass; |
| 218 Library stubLibrary; |
222 | 219 |
223 Library stubLibrary = new Library(Uri.parse('dart:core')) | 220 TypeParameter classTypeParameter; |
224 ..addClass(objectClass); | |
225 | 221 |
226 TypeParameter classTypeParameter = makeTypeParameter('T'); | 222 Library enclosingLibrary; |
| 223 Class enclosingClass; |
| 224 Procedure enclosingMember; |
227 | 225 |
228 Class otherClass = new Class( | 226 Class otherClass; |
229 name: 'OtherClass', | |
230 typeParameters: [makeTypeParameter('OtherT')], | |
231 supertype: objectClass.asRawSupertype); | |
232 | 227 |
233 Program makeProgram(TreeNode makeBody()) { | 228 void addNode(TreeNode node) { |
234 var node = makeBody(); | 229 if (node is Expression) { |
235 if (node is Expression) { | 230 addExpression(node); |
236 node = new ReturnStatement(node); | 231 } else if (node is Statement) { |
| 232 addStatement(node); |
| 233 } else if (node is Member) { |
| 234 addClassMember(node); |
| 235 } else if (node is Class) { |
| 236 addClass(node); |
| 237 } |
237 } | 238 } |
238 if (node is Statement) { | 239 |
239 node = new FunctionNode(node); | 240 void addExpression(Expression node) { |
| 241 addStatement(new ReturnStatement(node)); |
240 } | 242 } |
241 if (node is FunctionNode) { | 243 |
242 node = new Procedure(new Name('test'), ProcedureKind.Method, node); | 244 void addStatement(Statement node) { |
| 245 var function = enclosingMember.function; |
| 246 function.body = node..parent = function; |
243 } | 247 } |
244 if (node is Member) { | 248 |
245 node = new Class( | 249 void addClassMember(Member node) { |
246 name: 'Test', | 250 enclosingClass.addMember(node); |
| 251 } |
| 252 |
| 253 void addTopLevelMember(Member node) { |
| 254 enclosingLibrary.addMember(node); |
| 255 } |
| 256 |
| 257 void addClass(Class node) { |
| 258 enclosingLibrary.addClass(node); |
| 259 } |
| 260 |
| 261 VariableDeclaration makeVariable() => new VariableDeclaration(null); |
| 262 |
| 263 TypeParameter makeTypeParameter([String name]) { |
| 264 return new TypeParameter(name, new InterfaceType(objectClass)); |
| 265 } |
| 266 |
| 267 TestHarness() { |
| 268 setupProgram(); |
| 269 } |
| 270 |
| 271 void setupProgram() { |
| 272 program = new Program(); |
| 273 stubLibrary = new Library(Uri.parse('dart:core')); |
| 274 program.libraries.add(stubLibrary..parent = program); |
| 275 stubLibrary.name = 'dart.core'; |
| 276 objectClass = new Class(name: 'Object'); |
| 277 stubLibrary.addClass(objectClass); |
| 278 enclosingLibrary = new Library(Uri.parse('file://test.dart')); |
| 279 program.libraries.add(enclosingLibrary..parent = program); |
| 280 enclosingLibrary.name = 'test_lib'; |
| 281 classTypeParameter = makeTypeParameter('T'); |
| 282 enclosingClass = new Class( |
| 283 name: 'TestClass', |
247 typeParameters: [classTypeParameter], | 284 typeParameters: [classTypeParameter], |
248 supertype: objectClass.asRawSupertype)..addMember(node); | 285 supertype: objectClass.asRawSupertype); |
| 286 enclosingLibrary.addClass(enclosingClass); |
| 287 enclosingMember = new Procedure(new Name('test'), ProcedureKind.Method, |
| 288 new FunctionNode(new EmptyStatement())); |
| 289 enclosingClass.addMember(enclosingMember); |
| 290 otherClass = new Class( |
| 291 name: 'OtherClass', |
| 292 typeParameters: [makeTypeParameter('OtherT')], |
| 293 supertype: objectClass.asRawSupertype); |
| 294 enclosingLibrary.addClass(otherClass); |
249 } | 295 } |
250 if (node is Class) { | |
251 node = | |
252 new Library(Uri.parse('test.dart'), classes: <Class>[node, otherClass]); | |
253 } | |
254 if (node is Library) { | |
255 node = new Program(<Library>[node, stubLibrary]); | |
256 } | |
257 assert(node is Program); | |
258 return node; | |
259 } | 296 } |
260 | 297 |
261 negativeTest(String name, TreeNode makeBody()) { | 298 negativeTest(String name, TreeNode makeTestCase(TestHarness test)) { |
262 test(name, () { | 299 test(name, () { |
263 checkHasError(makeProgram(makeBody)); | 300 var test = new TestHarness(); |
| 301 test.addNode(makeTestCase(test)); |
| 302 checkHasError(test.program); |
264 }); | 303 }); |
265 } | 304 } |
266 | 305 |
267 positiveTest(String name, TreeNode makeBody()) { | 306 positiveTest(String name, TreeNode makeTestCase(TestHarness test)) { |
268 test(name, () { | 307 test(name, () { |
269 verifyProgram(makeProgram(makeBody)); | 308 var test = new TestHarness(); |
| 309 test.addNode(makeTestCase(test)); |
| 310 verifyProgram(test.program); |
270 }); | 311 }); |
271 } | 312 } |
272 | |
273 VariableDeclaration makeVariable() => new VariableDeclaration(null); | |
274 | |
275 TypeParameter makeTypeParameter([String name]) { | |
276 return new TypeParameter(name, new InterfaceType(objectClass)); | |
277 } | |
OLD | NEW |