OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 | 4 |
5 library constant_expression_test; | 5 library constant_expression_test; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'package:async_helper/async_helper.dart'; | 8 import 'package:async_helper/async_helper.dart'; |
9 import 'package:expect/expect.dart'; | 9 import 'package:expect/expect.dart'; |
10 import 'package:compiler/src/constants/expressions.dart'; | 10 import 'package:compiler/src/constants/expressions.dart'; |
11 import 'package:compiler/src/compiler.dart'; | 11 import 'package:compiler/src/compiler.dart'; |
12 import 'package:compiler/src/elements/elements.dart'; | 12 import 'package:compiler/src/elements/elements.dart'; |
13 import 'memory_compiler.dart'; | 13 import 'memory_compiler.dart'; |
14 | 14 |
15 class TestData { | 15 class TestData { |
16 /// Declarations needed for the [constants]. | 16 /// Declarations needed for the [constants]. |
17 final String declarations; | 17 final String declarations; |
| 18 |
18 /// Tested constants. | 19 /// Tested constants. |
19 final List constants; | 20 final List constants; |
20 | 21 |
21 const TestData(this.declarations, this.constants); | 22 const TestData(this.declarations, this.constants); |
22 } | 23 } |
23 | 24 |
24 class ConstantData { | 25 class ConstantData { |
25 /// Source code for the constant expression. | 26 /// Source code for the constant expression. |
26 final String code; | 27 final String code; |
| 28 |
27 /// The expected constant expression kind. | 29 /// The expected constant expression kind. |
28 final ConstantExpressionKind kind; | 30 final ConstantExpressionKind kind; |
| 31 |
29 /// ConstantExpression.getText() result if different from [code]. | 32 /// ConstantExpression.getText() result if different from [code]. |
30 final String text; | 33 final String text; |
| 34 |
31 /// The expected instance type for ConstructedConstantExpression. | 35 /// The expected instance type for ConstructedConstantExpression. |
32 final String type; | 36 final String type; |
| 37 |
33 /// The expected instance fields for ConstructedConstantExpression. | 38 /// The expected instance fields for ConstructedConstantExpression. |
34 final Map<String, String> fields; | 39 final Map<String, String> fields; |
35 | 40 |
36 const ConstantData(String code, | 41 const ConstantData(String code, this.kind, |
37 this.kind, | 42 {String text, this.type, this.fields}) |
38 {String text, | |
39 this.type, | |
40 this.fields}) | |
41 : this.code = code, | 43 : this.code = code, |
42 this.text = text != null ? text : code; | 44 this.text = text != null ? text : code; |
43 } | 45 } |
44 | 46 |
45 const List<TestData> DATA = const [ | 47 const List<TestData> DATA = const [ |
46 const TestData('', const [ | 48 const TestData('', const [ |
47 const ConstantData('null', ConstantExpressionKind.NULL), | 49 const ConstantData('null', ConstantExpressionKind.NULL), |
48 const ConstantData('false', ConstantExpressionKind.BOOL), | 50 const ConstantData('false', ConstantExpressionKind.BOOL), |
49 const ConstantData('true', ConstantExpressionKind.BOOL), | 51 const ConstantData('true', ConstantExpressionKind.BOOL), |
50 const ConstantData('0', ConstantExpressionKind.INT), | 52 const ConstantData('0', ConstantExpressionKind.INT), |
51 const ConstantData('0.0', ConstantExpressionKind.DOUBLE), | 53 const ConstantData('0.0', ConstantExpressionKind.DOUBLE), |
52 const ConstantData('"foo"', ConstantExpressionKind.STRING), | 54 const ConstantData('"foo"', ConstantExpressionKind.STRING), |
53 const ConstantData('1 + 2', ConstantExpressionKind.BINARY), | 55 const ConstantData('1 + 2', ConstantExpressionKind.BINARY), |
54 const ConstantData('1 == 2', ConstantExpressionKind.BINARY), | 56 const ConstantData('1 == 2', ConstantExpressionKind.BINARY), |
55 const ConstantData('1 != 2', ConstantExpressionKind.BINARY), | 57 const ConstantData('1 != 2', ConstantExpressionKind.BINARY), |
56 const ConstantData('1 ?? 2', ConstantExpressionKind.BINARY), | 58 const ConstantData('1 ?? 2', ConstantExpressionKind.BINARY), |
57 const ConstantData('-(1)', ConstantExpressionKind.UNARY, text: '-1'), | 59 const ConstantData('-(1)', ConstantExpressionKind.UNARY, text: '-1'), |
58 const ConstantData('"foo".length', ConstantExpressionKind.STRING_LENGTH), | 60 const ConstantData('"foo".length', ConstantExpressionKind.STRING_LENGTH), |
59 const ConstantData('identical(0, 1)', ConstantExpressionKind.IDENTICAL), | 61 const ConstantData('identical(0, 1)', ConstantExpressionKind.IDENTICAL), |
60 const ConstantData('"a" "b"', ConstantExpressionKind.CONCATENATE, | 62 const ConstantData('"a" "b"', ConstantExpressionKind.CONCATENATE, |
61 text: '"ab"'), | 63 text: '"ab"'), |
62 const ConstantData('identical', ConstantExpressionKind.FUNCTION), | 64 const ConstantData('identical', ConstantExpressionKind.FUNCTION), |
63 const ConstantData('true ? 0 : 1', ConstantExpressionKind.CONDITIONAL), | 65 const ConstantData('true ? 0 : 1', ConstantExpressionKind.CONDITIONAL), |
64 const ConstantData('proxy', ConstantExpressionKind.VARIABLE), | 66 const ConstantData('proxy', ConstantExpressionKind.VARIABLE), |
65 const ConstantData('Object', ConstantExpressionKind.TYPE), | 67 const ConstantData('Object', ConstantExpressionKind.TYPE), |
66 const ConstantData('#name', ConstantExpressionKind.SYMBOL), | 68 const ConstantData('#name', ConstantExpressionKind.SYMBOL), |
67 const ConstantData('const [0, 1]', ConstantExpressionKind.LIST), | 69 const ConstantData('const [0, 1]', ConstantExpressionKind.LIST), |
68 const ConstantData('const <int>[0, 1]', ConstantExpressionKind.LIST), | 70 const ConstantData('const <int>[0, 1]', ConstantExpressionKind.LIST), |
69 const ConstantData('const {0: 1, 2: 3}', ConstantExpressionKind.MAP), | 71 const ConstantData('const {0: 1, 2: 3}', ConstantExpressionKind.MAP), |
70 const ConstantData('const <int, int>{0: 1, 2: 3}', | |
71 ConstantExpressionKind.MAP), | |
72 const ConstantData( | 72 const ConstantData( |
73 'const bool.fromEnvironment("foo", defaultValue: false)', | 73 'const <int, int>{0: 1, 2: 3}', ConstantExpressionKind.MAP), |
| 74 const ConstantData('const bool.fromEnvironment("foo", defaultValue: false)', |
74 ConstantExpressionKind.BOOL_FROM_ENVIRONMENT), | 75 ConstantExpressionKind.BOOL_FROM_ENVIRONMENT), |
75 const ConstantData( | 76 const ConstantData('const int.fromEnvironment("foo", defaultValue: 42)', |
76 'const int.fromEnvironment("foo", defaultValue: 42)', | |
77 ConstantExpressionKind.INT_FROM_ENVIRONMENT), | 77 ConstantExpressionKind.INT_FROM_ENVIRONMENT), |
78 const ConstantData( | 78 const ConstantData( |
79 'const String.fromEnvironment("foo", defaultValue: "bar")', | 79 'const String.fromEnvironment("foo", defaultValue: "bar")', |
80 ConstantExpressionKind.STRING_FROM_ENVIRONMENT), | 80 ConstantExpressionKind.STRING_FROM_ENVIRONMENT), |
81 ]), | 81 ]), |
82 const TestData(''' | 82 const TestData( |
| 83 ''' |
83 class A { | 84 class A { |
84 const A(); | 85 const A(); |
85 } | 86 } |
86 class B { | 87 class B { |
87 final field1; | 88 final field1; |
88 const B(this.field1); | 89 const B(this.field1); |
89 } | 90 } |
90 class C extends B { | 91 class C extends B { |
91 final field2; | 92 final field2; |
92 const C({field1: 42, this.field2: false}) : super(field1); | 93 const C({field1: 42, this.field2: false}) : super(field1); |
93 const C.named([field = false]) : this(field1: field, field2: field); | 94 const C.named([field = false]) : this(field1: field, field2: field); |
94 } | 95 } |
95 ''', const [ | 96 ''', |
96 const ConstantData('const Object()', | 97 const [ |
97 ConstantExpressionKind.CONSTRUCTED, | 98 const ConstantData('const Object()', ConstantExpressionKind.CONSTRUCTED, |
98 type: 'Object', fields: const {}), | 99 type: 'Object', fields: const {}), |
99 const ConstantData('const A()', | 100 const ConstantData('const A()', ConstantExpressionKind.CONSTRUCTED, |
100 ConstantExpressionKind.CONSTRUCTED, | 101 type: 'A', fields: const {}), |
101 type: 'A', fields: const {}), | 102 const ConstantData('const B(0)', ConstantExpressionKind.CONSTRUCTED, |
102 const ConstantData('const B(0)', | 103 type: 'B', fields: const {'field(B#field1)': '0'}), |
103 ConstantExpressionKind.CONSTRUCTED, | 104 const ConstantData( |
104 type: 'B', | 105 'const B(const A())', ConstantExpressionKind.CONSTRUCTED, |
105 fields: const {'field(B#field1)': '0'}), | 106 type: 'B', fields: const {'field(B#field1)': 'const A()'}), |
106 const ConstantData('const B(const A())', | 107 const ConstantData('const C()', ConstantExpressionKind.CONSTRUCTED, |
107 ConstantExpressionKind.CONSTRUCTED, | 108 type: 'C', |
108 type: 'B', | 109 fields: const { |
109 fields: const {'field(B#field1)': 'const A()'}), | 110 'field(B#field1)': '42', |
110 const ConstantData('const C()', | 111 'field(C#field2)': 'false', |
111 ConstantExpressionKind.CONSTRUCTED, | 112 }), |
112 type: 'C', | 113 const ConstantData( |
113 fields: const { | 114 'const C(field1: 87)', ConstantExpressionKind.CONSTRUCTED, |
114 'field(B#field1)': '42', | 115 type: 'C', |
115 'field(C#field2)': 'false', | 116 fields: const { |
116 }), | 117 'field(B#field1)': '87', |
117 const ConstantData('const C(field1: 87)', | 118 'field(C#field2)': 'false', |
118 ConstantExpressionKind.CONSTRUCTED, | 119 }), |
119 type: 'C', | 120 const ConstantData( |
120 fields: const { | 121 'const C(field2: true)', ConstantExpressionKind.CONSTRUCTED, |
121 'field(B#field1)': '87', | 122 type: 'C', |
122 'field(C#field2)': 'false', | 123 fields: const { |
123 }), | 124 'field(B#field1)': '42', |
124 const ConstantData('const C(field2: true)', | 125 'field(C#field2)': 'true', |
125 ConstantExpressionKind.CONSTRUCTED, | 126 }), |
126 type: 'C', | 127 const ConstantData( |
127 fields: const { | 128 'const C.named()', ConstantExpressionKind.CONSTRUCTED, |
128 'field(B#field1)': '42', | 129 type: 'C', |
129 'field(C#field2)': 'true', | 130 fields: const { |
130 }), | 131 'field(B#field1)': 'false', |
131 const ConstantData('const C.named()', | 132 'field(C#field2)': 'false', |
132 ConstantExpressionKind.CONSTRUCTED, | 133 }), |
133 type: 'C', | 134 const ConstantData( |
134 fields: const { | 135 'const C.named(87)', ConstantExpressionKind.CONSTRUCTED, |
135 'field(B#field1)': 'false', | 136 type: 'C', |
136 'field(C#field2)': 'false', | 137 fields: const { |
137 }), | 138 'field(B#field1)': '87', |
138 const ConstantData('const C.named(87)', | 139 'field(C#field2)': '87', |
139 ConstantExpressionKind.CONSTRUCTED, | 140 }), |
140 type: 'C', | 141 ]), |
141 fields: const { | 142 const TestData( |
142 'field(B#field1)': '87', | 143 ''' |
143 'field(C#field2)': '87', | |
144 }), | |
145 ]), | |
146 const TestData(''' | |
147 class A<T> implements B { | 144 class A<T> implements B { |
148 final field1; | 145 final field1; |
149 const A({this.field1:42}); | 146 const A({this.field1:42}); |
150 } | 147 } |
151 class B<S> implements C { | 148 class B<S> implements C { |
152 const factory B({field1}) = A<B<S>>; | 149 const factory B({field1}) = A<B<S>>; |
153 // TODO(johnniwinther): Enable this when the constructor evaluator doesn't | 150 // TODO(johnniwinther): Enable this when the constructor evaluator doesn't |
154 // crash: | 151 // crash: |
155 const factory B.named() = A<S>; | 152 const factory B.named() = A<S>; |
156 } | 153 } |
157 class C<U> { | 154 class C<U> { |
158 const factory C({field1}) = A<B<double>>; | 155 const factory C({field1}) = A<B<double>>; |
159 } | 156 } |
160 ''', const [ | 157 ''', |
161 const ConstantData('const A()', ConstantExpressionKind.CONSTRUCTED, | 158 const [ |
162 type: 'A<dynamic>', | 159 const ConstantData('const A()', ConstantExpressionKind.CONSTRUCTED, |
163 fields: const {'field(A#field1)': '42'}), | 160 type: 'A<dynamic>', fields: const {'field(A#field1)': '42'}), |
164 const ConstantData('const A<int>(field1: 87)', | 161 const ConstantData( |
165 ConstantExpressionKind.CONSTRUCTED, | 162 'const A<int>(field1: 87)', ConstantExpressionKind.CONSTRUCTED, |
166 type: 'A<int>', | 163 type: 'A<int>', fields: const {'field(A#field1)': '87'}), |
167 fields: const {'field(A#field1)': '87'}), | 164 const ConstantData('const B()', ConstantExpressionKind.CONSTRUCTED, |
168 const ConstantData('const B()', ConstantExpressionKind.CONSTRUCTED, | 165 type: 'A<B<dynamic>>', |
169 type: 'A<B<dynamic>>', | 166 fields: const { |
170 fields: const { | 167 'field(A#field1)': '42', |
171 'field(A#field1)': '42', | 168 }), |
172 }), | 169 const ConstantData('const B<int>()', ConstantExpressionKind.CONSTRUCTED, |
173 const ConstantData('const B<int>()', ConstantExpressionKind.CONSTRUCTED, | 170 type: 'A<B<int>>', |
174 type: 'A<B<int>>', | 171 fields: const { |
175 fields: const { | 172 'field(A#field1)': '42', |
176 'field(A#field1)': '42', | 173 }), |
177 }), | 174 const ConstantData( |
178 const ConstantData('const B<int>(field1: 87)', | 175 'const B<int>(field1: 87)', ConstantExpressionKind.CONSTRUCTED, |
179 ConstantExpressionKind.CONSTRUCTED, | 176 type: 'A<B<int>>', |
180 type: 'A<B<int>>', | 177 fields: const { |
181 fields: const { | 178 'field(A#field1)': '87', |
182 'field(A#field1)': '87', | 179 }), |
183 }), | 180 const ConstantData( |
184 const ConstantData('const C<int>(field1: 87)', | 181 'const C<int>(field1: 87)', ConstantExpressionKind.CONSTRUCTED, |
185 ConstantExpressionKind.CONSTRUCTED, | 182 type: 'A<B<double>>', |
186 type: 'A<B<double>>', | 183 fields: const { |
187 fields: const { | 184 'field(A#field1)': '87', |
188 'field(A#field1)': '87', | 185 }), |
189 }), | 186 const ConstantData( |
190 const ConstantData('const B<int>.named()', | 187 'const B<int>.named()', ConstantExpressionKind.CONSTRUCTED, |
191 ConstantExpressionKind.CONSTRUCTED, | 188 type: 'A<int>', |
192 type: 'A<int>', | 189 fields: const { |
193 fields: const { | 190 'field(A#field1)': '42', |
194 'field(A#field1)': '42', | 191 }), |
195 }), | 192 ]), |
196 ]), | |
197 ]; | 193 ]; |
198 | 194 |
199 main() { | 195 main() { |
200 asyncTest(() => Future.forEach(DATA, testData)); | 196 asyncTest(() => Future.forEach(DATA, testData)); |
201 } | 197 } |
202 | 198 |
203 Future testData(TestData data) async { | 199 Future testData(TestData data) async { |
204 StringBuffer sb = new StringBuffer(); | 200 StringBuffer sb = new StringBuffer(); |
205 sb.write('${data.declarations}\n'); | 201 sb.write('${data.declarations}\n'); |
206 Map constants = {}; | 202 Map constants = {}; |
207 data.constants.forEach((ConstantData constantData) { | 203 data.constants.forEach((ConstantData constantData) { |
208 String name = 'c${constants.length}'; | 204 String name = 'c${constants.length}'; |
209 sb.write('const $name = ${constantData.code};\n'); | 205 sb.write('const $name = ${constantData.code};\n'); |
210 constants[name] = constantData; | 206 constants[name] = constantData; |
211 }); | 207 }); |
212 sb.write('main() {}\n'); | 208 sb.write('main() {}\n'); |
213 String source = sb.toString(); | 209 String source = sb.toString(); |
214 CompilationResult result = await runCompiler( | 210 CompilationResult result = await runCompiler( |
215 memorySourceFiles: {'main.dart': source}, | 211 memorySourceFiles: {'main.dart': source}, options: ['--analyze-all']); |
216 options: ['--analyze-all']); | |
217 Compiler compiler = result.compiler; | 212 Compiler compiler = result.compiler; |
218 var library = compiler.mainApp; | 213 var library = compiler.mainApp; |
219 constants.forEach((String name, ConstantData data) { | 214 constants.forEach((String name, ConstantData data) { |
220 FieldElement field = library.localLookup(name); | 215 FieldElement field = library.localLookup(name); |
221 var constant = field.constant; | 216 var constant = field.constant; |
222 Expect.equals(data.kind, constant.kind, | 217 Expect.equals( |
| 218 data.kind, |
| 219 constant.kind, |
223 "Unexpected kind '${constant.kind}' for contant " | 220 "Unexpected kind '${constant.kind}' for contant " |
224 "`${constant.toDartText()}`, expected '${data.kind}'."); | 221 "`${constant.toDartText()}`, expected '${data.kind}'."); |
225 Expect.equals(data.text, constant.toDartText(), | 222 Expect.equals( |
| 223 data.text, |
| 224 constant.toDartText(), |
226 "Unexpected text '${constant.toDartText()}' for contant, " | 225 "Unexpected text '${constant.toDartText()}' for contant, " |
227 "expected '${data.text}'."); | 226 "expected '${data.text}'."); |
228 if (data.type != null) { | 227 if (data.type != null) { |
229 String instanceType = constant.computeInstanceType().toString(); | 228 String instanceType = constant.computeInstanceType().toString(); |
230 Expect.equals(data.type, instanceType, | 229 Expect.equals( |
| 230 data.type, |
| 231 instanceType, |
231 "Unexpected type '$instanceType' for contant " | 232 "Unexpected type '$instanceType' for contant " |
232 "`${constant.toDartText()}`, expected '${data.type}'."); | 233 "`${constant.toDartText()}`, expected '${data.type}'."); |
233 } | 234 } |
234 if (data.fields != null) { | 235 if (data.fields != null) { |
235 Map instanceFields = constant.computeInstanceFields(); | 236 Map instanceFields = constant.computeInstanceFields(); |
236 Expect.equals(data.fields.length, instanceFields.length, | 237 Expect.equals( |
| 238 data.fields.length, |
| 239 instanceFields.length, |
237 "Unexpected field count ${instanceFields.length} for contant " | 240 "Unexpected field count ${instanceFields.length} for contant " |
238 "`${constant.toDartText()}`, expected '${data.fields.length}'."); | 241 "`${constant.toDartText()}`, expected '${data.fields.length}'."); |
239 instanceFields.forEach((field, expression) { | 242 instanceFields.forEach((field, expression) { |
240 String name = '$field'; | 243 String name = '$field'; |
241 String expression = instanceFields[field].toDartText(); | 244 String expression = instanceFields[field].toDartText(); |
242 String expected = data.fields[name]; | 245 String expected = data.fields[name]; |
243 Expect.equals(expected, expression, | 246 Expect.equals( |
| 247 expected, |
| 248 expression, |
244 "Unexpected field expression ${expression} for field '$name' in " | 249 "Unexpected field expression ${expression} for field '$name' in " |
245 "contant `${constant.toDartText()}`, expected '${expected}'."); | 250 "contant `${constant.toDartText()}`, expected '${expected}'."); |
246 }); | 251 }); |
247 } | 252 } |
248 }); | 253 }); |
249 } | 254 } |
OLD | NEW |