1 | // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
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. |
4 | |
5 | #include "vm/compiler/backend/sexpression.h" |
6 | |
7 | #include <cmath> |
8 | #include "platform/assert.h" |
9 | #include "vm/unit_test.h" |
10 | |
11 | namespace dart { |
12 | |
13 | #define EXPECT_SEXP_PARSE_ERROR(sexp, parser, pos, message) \ |
14 | do { \ |
15 | if (sexp != nullptr) { \ |
16 | dart::Expect(__FILE__, __LINE__) \ |
17 | .Fail("parse unexpectedly succeeded for \"%s\"", parser.Input()); \ |
18 | } \ |
19 | EXPECT_EQ(pos, parser.error_pos()); \ |
20 | EXPECT_STREQ(message, parser.error_message()); \ |
21 | } while (false); |
22 | |
23 | #define EXPECT_SEXP_PARSE_SUCCESS(sexp, parser) \ |
24 | do { \ |
25 | if (sexp == nullptr) { \ |
26 | EXPECT_NOTNULL(parser.error_message()); \ |
27 | dart::Expect(__FILE__, __LINE__) \ |
28 | .Fail("parse unexpectedly failed at \"%s\": %" Pd ": %s", \ |
29 | parser.Input() + parser.error_pos(), parser.error_pos(), \ |
30 | parser.error_message()); \ |
31 | } \ |
32 | } while (false); |
33 | |
34 | static const char* const shared_sexp_cstr = |
35 | "(def v0 (Constant 3) { type (CompileType 147 { nullable false, name " |
36 | "\"T{Smi}\"}), })" ; |
37 | |
38 | static void CheckDeserializedSExpParts(SExpression* sexp) { |
39 | EXPECT_NOTNULL(sexp); |
40 | EXPECT(sexp->IsList()); |
41 | SExpList* list = sexp->AsList(); |
42 | EXPECT_EQ(3, list->Length()); |
43 | EXPECT_NOTNULL(list->At(0)); |
44 | EXPECT(list->At(0)->IsSymbol()); |
45 | EXPECT_STREQ("def" , list->At(0)->AsSymbol()->value()); |
46 | EXPECT_NOTNULL(list->At(1)); |
47 | EXPECT(list->At(1)->IsSymbol()); |
48 | EXPECT_STREQ("v0" , list->At(1)->AsSymbol()->value()); |
49 | EXPECT_NOTNULL(list->At(2)); |
50 | EXPECT(list->At(2)->IsList()); |
51 | |
52 | SExpList* sublist = list->At(2)->AsList(); |
53 | EXPECT_EQ(2, sublist->Length()); |
54 | EXPECT_NOTNULL(sublist->At(0)); |
55 | EXPECT(sublist->At(0)->IsSymbol()); |
56 | EXPECT_STREQ("Constant" , sublist->At(0)->AsSymbol()->value()); |
57 | EXPECT_NOTNULL(sublist->At(1)); |
58 | EXPECT(sublist->At(1)->IsInteger()); |
59 | EXPECT_EQ(3, sublist->At(1)->AsInteger()->value()); |
60 | EXPECT_EQ(0, sublist->ExtraLength()); |
61 | |
62 | EXPECT_EQ(1, list->ExtraLength()); |
63 | EXPECT(list->ExtraHasKey("type" )); |
64 | EXPECT(list->ExtraLookupValue("type" )->IsList()); |
65 | |
66 | SExpList* ctype = list->ExtraLookupValue("type" )->AsList(); |
67 | EXPECT_EQ(2, ctype->Length()); |
68 | EXPECT_NOTNULL(ctype->At(0)); |
69 | EXPECT(ctype->At(0)->IsSymbol()); |
70 | EXPECT_STREQ("CompileType" , ctype->At(0)->AsSymbol()->value()); |
71 | EXPECT_NOTNULL(ctype->At(1)); |
72 | EXPECT(ctype->At(1)->IsInteger()); |
73 | EXPECT_EQ(147, ctype->At(1)->AsInteger()->value()); |
74 | |
75 | EXPECT_EQ(2, ctype->ExtraLength()); |
76 | EXPECT(ctype->ExtraHasKey("nullable" )); |
77 | EXPECT(ctype->ExtraLookupValue("nullable" )->IsBool()); |
78 | EXPECT(!ctype->ExtraLookupValue("nullable" )->AsBool()->value()); |
79 | EXPECT(ctype->ExtraHasKey("name" )); |
80 | EXPECT(ctype->ExtraLookupValue("name" )->IsString()); |
81 | EXPECT_STREQ(ctype->ExtraLookupValue("name" )->AsString()->value(), "T{Smi}" ); |
82 | } |
83 | |
84 | ISOLATE_UNIT_TEST_CASE(DeserializeSExp) { |
85 | Zone* const zone = Thread::Current()->zone(); |
86 | SExpression* sexp = SExpression::FromCString(zone, shared_sexp_cstr); |
87 | CheckDeserializedSExpParts(sexp); |
88 | |
89 | // Treating escaped backslash appropriately so string is terminated. |
90 | { |
91 | const char* const cstr = "(def v0 (Constant 3) { foo \"123\\\\\" })" ; |
92 | SExpParser parser(zone, cstr, strlen(cstr)); |
93 | SExpression* const sexp = parser.Parse(); |
94 | EXPECT_SEXP_PARSE_SUCCESS(sexp, parser); |
95 | EXPECT(sexp->IsList()); |
96 | EXPECT_EQ(1, sexp->AsList()->ExtraLength()); |
97 | EXPECT(sexp->AsList()->ExtraHasKey("foo" )); |
98 | auto val = sexp->AsList()->ExtraLookupValue("foo" ); |
99 | EXPECT(val->IsString()); |
100 | EXPECT_STREQ("123\\" , val->AsString()->value()); |
101 | } |
102 | // Valid unicode escapes are properly handled. |
103 | { |
104 | const char* const cstr = |
105 | "(def v0 (Constant 3) { foo \"\\u0001\\u0020\\u0054\" })" ; |
106 | SExpParser parser(zone, cstr, strlen(cstr)); |
107 | SExpression* const sexp = parser.Parse(); |
108 | EXPECT_SEXP_PARSE_SUCCESS(sexp, parser); |
109 | EXPECT(sexp->IsList()); |
110 | EXPECT_EQ(1, sexp->AsList()->ExtraLength()); |
111 | EXPECT(sexp->AsList()->ExtraHasKey("foo" )); |
112 | auto val = sexp->AsList()->ExtraLookupValue("foo" ); |
113 | EXPECT(val->IsString()); |
114 | EXPECT_STREQ("\x01 T" , val->AsString()->value()); |
115 | } |
116 | } |
117 | |
118 | ISOLATE_UNIT_TEST_CASE(DeserializeSExpNumbers) { |
119 | Zone* const zone = Thread::Current()->zone(); |
120 | |
121 | // Negative integers are handled. |
122 | { |
123 | const char* const cstr = "(-4 -50 -1414243)" ; |
124 | SExpParser parser(zone, cstr, strlen(cstr)); |
125 | SExpression* const sexp = parser.Parse(); |
126 | EXPECT_SEXP_PARSE_SUCCESS(sexp, parser); |
127 | EXPECT(sexp->IsList()); |
128 | auto list = sexp->AsList(); |
129 | EXPECT_EQ(3, list->Length()); |
130 | EXPECT_EQ(0, list->ExtraLength()); |
131 | for (intptr_t i = 0; i < list->Length(); i++) { |
132 | EXPECT(list->At(i)->IsInteger()); |
133 | EXPECT(list->At(i)->AsInteger()->value() < 0); |
134 | } |
135 | } |
136 | |
137 | // Various decimal/exponent Doubles are appropriately handled. |
138 | { |
139 | const char* const cstr = "(1.05 0.05 .03 1e100 1e-100)" ; |
140 | SExpParser parser(zone, cstr, strlen(cstr)); |
141 | SExpression* const sexp = parser.Parse(); |
142 | EXPECT_SEXP_PARSE_SUCCESS(sexp, parser); |
143 | EXPECT(sexp->IsList()); |
144 | auto list = sexp->AsList(); |
145 | EXPECT_EQ(5, list->Length()); |
146 | EXPECT_EQ(0, list->ExtraLength()); |
147 | EXPECT(list->At(0)->IsDouble()); |
148 | double val = list->At(0)->AsDouble()->value(); |
149 | EXPECT(val > 1.04 && val < 1.06); |
150 | EXPECT(list->At(1)->IsDouble()); |
151 | val = list->At(1)->AsDouble()->value(); |
152 | EXPECT(val > 0.04 && val < 0.06); |
153 | EXPECT(list->At(2)->IsDouble()); |
154 | val = list->At(2)->AsDouble()->value(); |
155 | EXPECT(val > 0.02 && val < 0.04); |
156 | EXPECT(list->At(3)->IsDouble()); |
157 | val = list->At(3)->AsDouble()->value(); |
158 | EXPECT(val > 0.9e100 && val < 1.1e100); |
159 | EXPECT(list->At(4)->IsDouble()); |
160 | val = list->At(4)->AsDouble()->value(); |
161 | EXPECT(val > 0.9e-100 && val < 1.1e-100); |
162 | } |
163 | |
164 | // Special Double symbols are appropriately handled. |
165 | { |
166 | const char* const cstr = "(NaN Infinity -Infinity)" ; |
167 | SExpParser parser(zone, cstr, strlen(cstr)); |
168 | SExpression* const sexp = parser.Parse(); |
169 | EXPECT_SEXP_PARSE_SUCCESS(sexp, parser); |
170 | EXPECT(sexp->IsList()); |
171 | auto list = sexp->AsList(); |
172 | EXPECT_EQ(3, list->Length()); |
173 | EXPECT_EQ(0, list->ExtraLength()); |
174 | EXPECT(list->At(0)->IsDouble()); |
175 | double val = list->At(0)->AsDouble()->value(); |
176 | EXPECT(isnan(val)); |
177 | EXPECT(list->At(1)->IsDouble()); |
178 | val = list->At(1)->AsDouble()->value(); |
179 | EXPECT(val > 0.0); |
180 | EXPECT(isinf(val)); |
181 | EXPECT(list->At(2)->IsDouble()); |
182 | val = list->At(2)->AsDouble()->value(); |
183 | EXPECT(val < 0.0); |
184 | EXPECT(isinf(val)); |
185 | } |
186 | } |
187 | |
188 | ISOLATE_UNIT_TEST_CASE(DeserializeSExpRoundTrip) { |
189 | Zone* const zone = Thread::Current()->zone(); |
190 | SExpression* sexp = SExpression::FromCString(zone, shared_sexp_cstr); |
191 | |
192 | TextBuffer buf(100); |
193 | sexp->SerializeTo(zone, &buf, "" , 9999); |
194 | SExpression* round_trip = SExpression::FromCString(zone, buf.buffer()); |
195 | CheckDeserializedSExpParts(round_trip); |
196 | EXPECT(sexp->Equals(round_trip)); |
197 | |
198 | char* const old_serialization = buf.Steal(); |
199 | round_trip->SerializeTo(zone, &buf, "" , 9999); |
200 | char* const new_serialization = buf.buffer(); |
201 | EXPECT_STREQ(old_serialization, new_serialization); |
202 | free(old_serialization); |
203 | } |
204 | |
205 | ISOLATE_UNIT_TEST_CASE(DeserializeSExpMapsJoined) { |
206 | Zone* const zone = Thread::Current()->zone(); |
207 | // Same as shared_sexp_cstr except we split the map on the CompileType into |
208 | // two parts. |
209 | const char* const cstr = |
210 | "(def v0 (Constant 3) { type (CompileType { nullable false } 147 { name " |
211 | "\"T{Smi}\"}), })" ; |
212 | SExpression* sexp = SExpression::FromCString(zone, cstr); |
213 | CheckDeserializedSExpParts(sexp); |
214 | } |
215 | |
216 | ISOLATE_UNIT_TEST_CASE(DeserializeSExpFailures) { |
217 | Zone* const zone = Thread::Current()->zone(); |
218 | // Unterminated s-exp list |
219 | { |
220 | const char* const before_start = "(def v0 " ; |
221 | const char* const after_start = "(Constant 3" ; |
222 | const char* const cstr = |
223 | OS::SCreate(zone, "%s%s" , before_start, after_start); |
224 | const intptr_t start_pos = strlen(before_start); |
225 | const intptr_t error_pos = strlen(cstr); |
226 | SExpParser parser(zone, cstr, strlen(cstr)); |
227 | SExpression* const sexp = parser.Parse(); |
228 | const char* const expected_message = |
229 | OS::SCreate(zone, SExpParser::ErrorStrings::kOpenSExpList, start_pos); |
230 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
231 | } |
232 | // Non-symbol label in map pair |
233 | { |
234 | const char* const before_error = "(def v0 (Constant 3) { " ; |
235 | const intptr_t error_pos = strlen(before_error); |
236 | const char* const error = "3 4 })" ; |
237 | const char* const cstr = OS::SCreate(zone, "%s%s" , before_error, error); |
238 | SExpParser parser(zone, cstr, strlen(cstr)); |
239 | SExpression* const sexp = parser.Parse(); |
240 | const char* const expected_message = |
241 | SExpParser::ErrorStrings::kNonSymbolLabel; |
242 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
243 | } |
244 | // No values in a map pair |
245 | { |
246 | const char* const label = "foo" ; |
247 | const char* const before_error = |
248 | OS::SCreate(zone, "(def v0 (Constant 3) { %s " , label); |
249 | const intptr_t error_pos = strlen(before_error); |
250 | const char* const error = "})" ; |
251 | const char* const cstr = OS::SCreate(zone, "%s%s" , before_error, error); |
252 | SExpParser parser(zone, cstr, strlen(cstr)); |
253 | SExpression* const sexp = parser.Parse(); |
254 | const char* const expected_message = |
255 | OS::SCreate(zone, SExpParser::ErrorStrings::kNoMapValue, label); |
256 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
257 | } |
258 | // Multiple values in a map pair |
259 | { |
260 | const char* const label = "foo" ; |
261 | const char* const before_error = |
262 | OS::SCreate(zone, "(def v0 (Constant 3) { %s 4 " , label); |
263 | const intptr_t error_pos = strlen(before_error); |
264 | const char* const error = "5, })" ; |
265 | const char* const cstr = OS::SCreate(zone, "%s%s" , before_error, error); |
266 | SExpParser parser(zone, cstr, strlen(cstr)); |
267 | SExpression* const sexp = parser.Parse(); |
268 | const char* const expected_message = |
269 | OS::SCreate(zone, SExpParser::ErrorStrings::kExtraMapValue, label); |
270 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
271 | } |
272 | // Unterminated quoted string |
273 | { |
274 | const char* const before_string = |
275 | OS::SCreate(zone, "(def v0 (Constant 3) { foo " ); |
276 | const intptr_t string_pos = strlen(before_string); |
277 | const char* const error = "\"abc })" ; |
278 | const char* const cstr = OS::SCreate(zone, "%s%s" , before_string, error); |
279 | const intptr_t error_pos = strlen(cstr); |
280 | SExpParser parser(zone, cstr, strlen(cstr)); |
281 | SExpression* const sexp = parser.Parse(); |
282 | const char* const expected_message = |
283 | OS::SCreate(zone, SExpParser::ErrorStrings::kOpenString, string_pos); |
284 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
285 | } |
286 | // Unterminated extra info map |
287 | { |
288 | const char* const before_map = "(def v0 (Constant 3) " ; |
289 | const intptr_t map_pos = strlen(before_map); |
290 | const char* const map_start = "{ foo 3, " ; |
291 | const char* const before_error = |
292 | OS::SCreate(zone, "%s%s" , before_map, map_start); |
293 | const intptr_t error_pos = strlen(before_error); |
294 | const char* const error = ")" ; |
295 | const char* const cstr = OS::SCreate(zone, "%s%s" , before_error, error); |
296 | SExpParser parser(zone, cstr, strlen(cstr)); |
297 | SExpression* const sexp = parser.Parse(); |
298 | const char* const expected_message = |
299 | OS::SCreate(zone, SExpParser::ErrorStrings::kOpenMap, map_pos); |
300 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
301 | } |
302 | // Repeated extra info map label |
303 | { |
304 | const char* const label = "foo" ; |
305 | const char* const before_error = |
306 | OS::SCreate(zone, "(def v0 (Constant 3) { %s 3, " , label); |
307 | const intptr_t error_pos = strlen(before_error); |
308 | const char* const error = OS::SCreate(zone, "%s 4, })" , label); |
309 | const char* const cstr = OS::SCreate(zone, "%s%s" , before_error, error); |
310 | SExpParser parser(zone, cstr, strlen(cstr)); |
311 | SExpression* const sexp = parser.Parse(); |
312 | const char* const expected_message = |
313 | OS::SCreate(zone, SExpParser::ErrorStrings::kRepeatedMapLabel, label); |
314 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
315 | } |
316 | // Unicode escape with non-hex digits. |
317 | { |
318 | const char* const before_error = "(def v0 (Constant 3) { foo \"123" ; |
319 | const intptr_t error_pos = strlen(before_error); |
320 | const char* const error = "\\u12FG\" })" ; |
321 | const char* const cstr = OS::SCreate(zone, "%s%s" , before_error, error); |
322 | SExpParser parser(zone, cstr, strlen(cstr)); |
323 | SExpression* const sexp = parser.Parse(); |
324 | const char* const expected_message = |
325 | SExpParser::ErrorStrings::kBadUnicodeEscape; |
326 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
327 | } |
328 | // Unicode escape with less than four hex digits. |
329 | { |
330 | const char* const before_error = "(def v0 (Constant 3) { foo \"123" ; |
331 | const intptr_t error_pos = strlen(before_error); |
332 | const char* const error = "\\u12\" })" ; |
333 | const char* const cstr = OS::SCreate(zone, "%s%s" , before_error, error); |
334 | SExpParser parser(zone, cstr, strlen(cstr)); |
335 | SExpression* const sexp = parser.Parse(); |
336 | const char* const expected_message = |
337 | SExpParser::ErrorStrings::kBadUnicodeEscape; |
338 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
339 | } |
340 | // Treating backslashed quote appropriately to detect unterminated string |
341 | { |
342 | const char* const before_string = "(def v0 (Constant 3) { foo " ; |
343 | const intptr_t string_pos = strlen(before_string); |
344 | const char* const error = "\"123\\\" })" ; |
345 | const char* const cstr = OS::SCreate(zone, "%s%s" , before_string, error); |
346 | const intptr_t error_pos = strlen(cstr); |
347 | SExpParser parser(zone, cstr, strlen(cstr)); |
348 | SExpression* const sexp = parser.Parse(); |
349 | const char* const expected_message = |
350 | OS::SCreate(zone, SExpParser::ErrorStrings::kOpenString, string_pos); |
351 | EXPECT_SEXP_PARSE_ERROR(sexp, parser, error_pos, expected_message); |
352 | } |
353 | } |
354 | |
355 | } // namespace dart |
356 | |