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 {'field(B#field1)': '87', 'field(C#field2)': '87',}), |
137 }), | 138 ]), |
138 const ConstantData('const C.named(87)', | 139 const TestData( |
139 ConstantExpressionKind.CONSTRUCTED, | 140 ''' |
140 type: 'C', | |
141 fields: const { | |
142 'field(B#field1)': '87', | |
143 'field(C#field2)': '87', | |
144 }), | |
145 ]), | |
146 const TestData(''' | |
147 class A<T> implements B { | 141 class A<T> implements B { |
148 final field1; | 142 final field1; |
149 const A({this.field1:42}); | 143 const A({this.field1:42}); |
150 } | 144 } |
151 class B<S> implements C { | 145 class B<S> implements C { |
152 const factory B({field1}) = A<B<S>>; | 146 const factory B({field1}) = A<B<S>>; |
153 // TODO(johnniwinther): Enable this when the constructor evaluator doesn't | 147 // TODO(johnniwinther): Enable this when the constructor evaluator doesn't |
154 // crash: | 148 // crash: |
155 const factory B.named() = A<S>; | 149 const factory B.named() = A<S>; |
156 } | 150 } |
157 class C<U> { | 151 class C<U> { |
158 const factory C({field1}) = A<B<double>>; | 152 const factory C({field1}) = A<B<double>>; |
159 } | 153 } |
160 ''', const [ | 154 ''', |
161 const ConstantData('const A()', ConstantExpressionKind.CONSTRUCTED, | 155 const [ |
162 type: 'A<dynamic>', | 156 const ConstantData('const A()', ConstantExpressionKind.CONSTRUCTED, |
163 fields: const {'field(A#field1)': '42'}), | 157 type: 'A<dynamic>', fields: const {'field(A#field1)': '42'}), |
164 const ConstantData('const A<int>(field1: 87)', | 158 const ConstantData( |
165 ConstantExpressionKind.CONSTRUCTED, | 159 'const A<int>(field1: 87)', ConstantExpressionKind.CONSTRUCTED, |
166 type: 'A<int>', | 160 type: 'A<int>', fields: const {'field(A#field1)': '87'}), |
167 fields: const {'field(A#field1)': '87'}), | 161 const ConstantData('const B()', ConstantExpressionKind.CONSTRUCTED, |
168 const ConstantData('const B()', ConstantExpressionKind.CONSTRUCTED, | 162 type: 'A<B<dynamic>>', fields: const {'field(A#field1)': '42',}), |
169 type: 'A<B<dynamic>>', | 163 const ConstantData('const B<int>()', ConstantExpressionKind.CONSTRUCTED, |
170 fields: const { | 164 type: 'A<B<int>>', fields: const {'field(A#field1)': '42',}), |
171 'field(A#field1)': '42', | 165 const ConstantData( |
172 }), | 166 'const B<int>(field1: 87)', ConstantExpressionKind.CONSTRUCTED, |
173 const ConstantData('const B<int>()', ConstantExpressionKind.CONSTRUCTED, | 167 type: 'A<B<int>>', fields: const {'field(A#field1)': '87',}), |
174 type: 'A<B<int>>', | 168 const ConstantData( |
175 fields: const { | 169 'const C<int>(field1: 87)', ConstantExpressionKind.CONSTRUCTED, |
176 'field(A#field1)': '42', | 170 type: 'A<B<double>>', fields: const {'field(A#field1)': '87',}), |
177 }), | 171 const ConstantData( |
178 const ConstantData('const B<int>(field1: 87)', | 172 'const B<int>.named()', ConstantExpressionKind.CONSTRUCTED, |
179 ConstantExpressionKind.CONSTRUCTED, | 173 type: 'A<int>', fields: const {'field(A#field1)': '42',}), |
180 type: 'A<B<int>>', | 174 ]), |
181 fields: const { | |
182 'field(A#field1)': '87', | |
183 }), | |
184 const ConstantData('const C<int>(field1: 87)', | |
185 ConstantExpressionKind.CONSTRUCTED, | |
186 type: 'A<B<double>>', | |
187 fields: const { | |
188 'field(A#field1)': '87', | |
189 }), | |
190 const ConstantData('const B<int>.named()', | |
191 ConstantExpressionKind.CONSTRUCTED, | |
192 type: 'A<int>', | |
193 fields: const { | |
194 'field(A#field1)': '42', | |
195 }), | |
196 ]), | |
197 ]; | 175 ]; |
198 | 176 |
199 main() { | 177 main() { |
200 asyncTest(() => Future.forEach(DATA, testData)); | 178 asyncTest(() => Future.forEach(DATA, testData)); |
201 } | 179 } |
202 | 180 |
203 Future testData(TestData data) async { | 181 Future testData(TestData data) async { |
204 StringBuffer sb = new StringBuffer(); | 182 StringBuffer sb = new StringBuffer(); |
205 sb.write('${data.declarations}\n'); | 183 sb.write('${data.declarations}\n'); |
206 Map constants = {}; | 184 Map constants = {}; |
207 data.constants.forEach((ConstantData constantData) { | 185 data.constants.forEach((ConstantData constantData) { |
208 String name = 'c${constants.length}'; | 186 String name = 'c${constants.length}'; |
209 sb.write('const $name = ${constantData.code};\n'); | 187 sb.write('const $name = ${constantData.code};\n'); |
210 constants[name] = constantData; | 188 constants[name] = constantData; |
211 }); | 189 }); |
212 sb.write('main() {}\n'); | 190 sb.write('main() {}\n'); |
213 String source = sb.toString(); | 191 String source = sb.toString(); |
214 CompilationResult result = await runCompiler( | 192 CompilationResult result = await runCompiler( |
215 memorySourceFiles: {'main.dart': source}, | 193 memorySourceFiles: {'main.dart': source}, options: ['--analyze-all']); |
216 options: ['--analyze-all']); | |
217 Compiler compiler = result.compiler; | 194 Compiler compiler = result.compiler; |
218 var library = compiler.mainApp; | 195 var library = compiler.mainApp; |
219 constants.forEach((String name, ConstantData data) { | 196 constants.forEach((String name, ConstantData data) { |
220 FieldElement field = library.localLookup(name); | 197 FieldElement field = library.localLookup(name); |
221 var constant = field.constant; | 198 var constant = field.constant; |
222 Expect.equals(data.kind, constant.kind, | 199 Expect.equals( |
| 200 data.kind, |
| 201 constant.kind, |
223 "Unexpected kind '${constant.kind}' for contant " | 202 "Unexpected kind '${constant.kind}' for contant " |
224 "`${constant.toDartText()}`, expected '${data.kind}'."); | 203 "`${constant.toDartText()}`, expected '${data.kind}'."); |
225 Expect.equals(data.text, constant.toDartText(), | 204 Expect.equals( |
| 205 data.text, |
| 206 constant.toDartText(), |
226 "Unexpected text '${constant.toDartText()}' for contant, " | 207 "Unexpected text '${constant.toDartText()}' for contant, " |
227 "expected '${data.text}'."); | 208 "expected '${data.text}'."); |
228 if (data.type != null) { | 209 if (data.type != null) { |
229 String instanceType = constant.computeInstanceType().toString(); | 210 String instanceType = constant.computeInstanceType().toString(); |
230 Expect.equals(data.type, instanceType, | 211 Expect.equals( |
| 212 data.type, |
| 213 instanceType, |
231 "Unexpected type '$instanceType' for contant " | 214 "Unexpected type '$instanceType' for contant " |
232 "`${constant.toDartText()}`, expected '${data.type}'."); | 215 "`${constant.toDartText()}`, expected '${data.type}'."); |
233 } | 216 } |
234 if (data.fields != null) { | 217 if (data.fields != null) { |
235 Map instanceFields = constant.computeInstanceFields(); | 218 Map instanceFields = constant.computeInstanceFields(); |
236 Expect.equals(data.fields.length, instanceFields.length, | 219 Expect.equals( |
| 220 data.fields.length, |
| 221 instanceFields.length, |
237 "Unexpected field count ${instanceFields.length} for contant " | 222 "Unexpected field count ${instanceFields.length} for contant " |
238 "`${constant.toDartText()}`, expected '${data.fields.length}'."); | 223 "`${constant.toDartText()}`, expected '${data.fields.length}'."); |
239 instanceFields.forEach((field, expression) { | 224 instanceFields.forEach((field, expression) { |
240 String name = '$field'; | 225 String name = '$field'; |
241 String expression = instanceFields[field].toDartText(); | 226 String expression = instanceFields[field].toDartText(); |
242 String expected = data.fields[name]; | 227 String expected = data.fields[name]; |
243 Expect.equals(expected, expression, | 228 Expect.equals( |
| 229 expected, |
| 230 expression, |
244 "Unexpected field expression ${expression} for field '$name' in " | 231 "Unexpected field expression ${expression} for field '$name' in " |
245 "contant `${constant.toDartText()}`, expected '${expected}'."); | 232 "contant `${constant.toDartText()}`, expected '${expected}'."); |
246 }); | 233 }); |
247 } | 234 } |
248 }); | 235 }); |
249 } | 236 } |
OLD | NEW |