| 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 |