1 // Copyright 2017 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "xfa/fxfa/formcalc/cxfa_fmparser.h"
6
7 #include "core/fxcrt/widetext_buffer.h"
8 #include "testing/fxgc_unittest.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h"
11
12 class CXFA_FMParserTest : public FXGCUnitTest {};
13
TEST_F(CXFA_FMParserTest,Empty)14 TEST_F(CXFA_FMParserTest, Empty) {
15 CXFA_FMLexer lexer(L"");
16 CXFA_FMParser parser(heap(), &lexer);
17 CXFA_FMAST* ast = parser.Parse();
18 ASSERT_TRUE(ast);
19 EXPECT_FALSE(parser.HasError());
20
21 CXFA_FMToJavaScriptDepth::Reset();
22 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
23 ASSERT_TRUE(buf.has_value());
24 // TODO(dsinclair): This is a little weird .....
25 EXPECT_STREQ(L"// comments only", buf.value().MakeString().c_str());
26 }
27
TEST_F(CXFA_FMParserTest,CommentOnlyIsError)28 TEST_F(CXFA_FMParserTest, CommentOnlyIsError) {
29 CXFA_FMLexer lexer(L"; Just comment");
30 CXFA_FMParser parser(heap(), &lexer);
31 CXFA_FMAST* ast = parser.Parse();
32 ASSERT_TRUE(ast);
33 // TODO(dsinclair): This isn't allowed per the spec.
34 EXPECT_FALSE(parser.HasError());
35 // EXPECT_TRUE(parser.HasError());
36
37 CXFA_FMToJavaScriptDepth::Reset();
38 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
39 ASSERT_TRUE(buf.has_value());
40 EXPECT_STREQ(L"// comments only", buf.value().MakeString().c_str());
41 }
42
TEST_F(CXFA_FMParserTest,CommentThenValue)43 TEST_F(CXFA_FMParserTest, CommentThenValue) {
44 const wchar_t ret[] =
45 LR"***((function() {
46 let pfm_method_runner = function(obj, cb) {
47 if (pfm_rt.is_ary(obj)) {
48 let pfm_method_return = null;
49 for (var idx = obj.length -1; idx > 1; idx--) {
50 pfm_method_return = cb(obj[idx]);
51 }
52 return pfm_method_return;
53 }
54 return cb(obj);
55 };
56 var pfm_ret = null;
57 pfm_ret = 12;
58 return pfm_rt.get_val(pfm_ret);
59 }).call(this);)***";
60
61 CXFA_FMLexer lexer(L"; Just comment\n12");
62 CXFA_FMParser parser(heap(), &lexer);
63 CXFA_FMAST* ast = parser.Parse();
64 ASSERT_TRUE(ast);
65 EXPECT_FALSE(parser.HasError());
66
67 CXFA_FMToJavaScriptDepth::Reset();
68 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
69 ASSERT_TRUE(buf.has_value());
70 EXPECT_STREQ(ret, buf.value().MakeString().c_str());
71 }
72
TEST_F(CXFA_FMParserTest,Parse)73 TEST_F(CXFA_FMParserTest, Parse) {
74 const wchar_t input[] =
75 LR"***($ = Avg (-3, 5, -6, 12, -13);
76 $ = Avg (Table2..Row[*].Cell1);
77 if ($ ne -1)then
78 border.fill.color.value = "255,64,64";
79 elseif ($ ne -2) then
80 border.fill.color.value = "128,128,128";
81 else
82 border.fill.color.value = "20,170,13";
83 endif
84 $)***";
85
86 const wchar_t ret[] =
87 LR"***((function() {
88 let pfm_method_runner = function(obj, cb) {
89 if (pfm_rt.is_ary(obj)) {
90 let pfm_method_return = null;
91 for (var idx = obj.length -1; idx > 1; idx--) {
92 pfm_method_return = cb(obj[idx]);
93 }
94 return pfm_method_return;
95 }
96 return cb(obj);
97 };
98 var pfm_ret = null;
99 if (pfm_rt.is_obj(this))
100 {
101 pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.neg_op(3), 5, pfm_rt.neg_op(6), 12, pfm_rt.neg_op(13)));
102 }
103 if (pfm_rt.is_obj(this))
104 {
105 pfm_rt.asgn_val_op(this, pfm_rt.Avg(pfm_rt.dot_acc(pfm_rt.dotdot_acc(Table2, "Table2", "Row", 1), "", "Cell1", 0, 0)));
106 }
107 if (pfm_rt.get_val(pfm_rt.neq_op(this, pfm_rt.neg_op(1))))
108 {
109 if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
110 {
111 pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "255,64,64");
112 }
113 }
114 else if (pfm_rt.get_val(pfm_rt.neq_op(this, pfm_rt.neg_op(2))))
115 {
116 if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
117 {
118 pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "128,128,128");
119 }
120 }
121 else {
122 if (pfm_rt.is_obj(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0)))
123 {
124 pfm_rt.asgn_val_op(pfm_rt.dot_acc(pfm_rt.dot_acc(pfm_rt.dot_acc(border, "border", "fill", 0, 0), "", "color", 0, 0), "", "value", 0, 0), "20,170,13");
125 }
126 }
127 pfm_ret = this;
128 return pfm_rt.get_val(pfm_ret);
129 }).call(this);)***";
130
131 CXFA_FMLexer lexer(input);
132 CXFA_FMParser parser(heap(), &lexer);
133 CXFA_FMAST* ast = parser.Parse();
134 ASSERT_TRUE(ast);
135 EXPECT_FALSE(parser.HasError());
136
137 CXFA_FMToJavaScriptDepth::Reset();
138 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
139 ASSERT_TRUE(buf.has_value());
140 EXPECT_EQ(ret, buf.value().AsStringView());
141 }
142
TEST_F(CXFA_FMParserTest,MaxParseDepth)143 TEST_F(CXFA_FMParserTest, MaxParseDepth) {
144 CXFA_FMLexer lexer(L"foo(bar[baz(fizz[0])])");
145 CXFA_FMParser parser(heap(), &lexer);
146 parser.SetMaxParseDepthForTest(5);
147 EXPECT_FALSE(parser.Parse());
148 EXPECT_TRUE(parser.HasError());
149 }
150
TEST_F(CXFA_FMParserTest,chromium752201)151 TEST_F(CXFA_FMParserTest, chromium752201) {
152 CXFA_FMLexer lexer(
153 LR"***(fTep a
154 .#
155 fo@ =[=l)***");
156
157 CXFA_FMParser parser(heap(), &lexer);
158 EXPECT_FALSE(parser.Parse());
159 EXPECT_TRUE(parser.HasError());
160 }
161
TEST_F(CXFA_FMParserTest,MultipleAssignmentIsNotAllowed)162 TEST_F(CXFA_FMParserTest, MultipleAssignmentIsNotAllowed) {
163 CXFA_FMLexer lexer(L"(a=(b=t))=u");
164 CXFA_FMParser parser(heap(), &lexer);
165 CXFA_FMAST* ast = parser.Parse();
166 ASSERT_TRUE(!ast);
167 EXPECT_TRUE(parser.HasError());
168 }
169
TEST_F(CXFA_FMParserTest,ParseFuncWithParams)170 TEST_F(CXFA_FMParserTest, ParseFuncWithParams) {
171 const wchar_t input[] =
172 LR"***(func MyFunction(param1, param2) do
173 param1 * param2
174 endfunc)***";
175
176 const wchar_t ret[] =
177 LR"***((function() {
178 let pfm_method_runner = function(obj, cb) {
179 if (pfm_rt.is_ary(obj)) {
180 let pfm_method_return = null;
181 for (var idx = obj.length -1; idx > 1; idx--) {
182 pfm_method_return = cb(obj[idx]);
183 }
184 return pfm_method_return;
185 }
186 return cb(obj);
187 };
188 var pfm_ret = null;
189 function MyFunction(param1, param2) {
190 var pfm_ret = null;
191 pfm_ret = pfm_rt.mul_op(param1, param2);
192 return pfm_ret;
193 }
194 return pfm_rt.get_val(pfm_ret);
195 }).call(this);)***";
196
197 CXFA_FMLexer lexer(input);
198 CXFA_FMParser parser(heap(), &lexer);
199 CXFA_FMAST* ast = parser.Parse();
200 ASSERT_TRUE(ast);
201 EXPECT_FALSE(parser.HasError());
202
203 CXFA_FMToJavaScriptDepth::Reset();
204 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
205 ASSERT_TRUE(buf.has_value());
206 EXPECT_STREQ(ret, buf.value().MakeString().c_str());
207 }
208
TEST_F(CXFA_FMParserTest,ParseFuncWithoutParams)209 TEST_F(CXFA_FMParserTest, ParseFuncWithoutParams) {
210 const wchar_t input[] =
211 LR"***(func MyFunction() do
212 42
213 endfunc)***";
214
215 const wchar_t ret[] =
216 LR"***((function() {
217 let pfm_method_runner = function(obj, cb) {
218 if (pfm_rt.is_ary(obj)) {
219 let pfm_method_return = null;
220 for (var idx = obj.length -1; idx > 1; idx--) {
221 pfm_method_return = cb(obj[idx]);
222 }
223 return pfm_method_return;
224 }
225 return cb(obj);
226 };
227 var pfm_ret = null;
228 function MyFunction() {
229 var pfm_ret = null;
230 pfm_ret = 42;
231 return pfm_ret;
232 }
233 return pfm_rt.get_val(pfm_ret);
234 }).call(this);)***";
235
236 CXFA_FMLexer lexer(input);
237 CXFA_FMParser parser(heap(), &lexer);
238 CXFA_FMAST* ast = parser.Parse();
239 ASSERT_TRUE(ast);
240 EXPECT_FALSE(parser.HasError());
241
242 CXFA_FMToJavaScriptDepth::Reset();
243 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
244 ASSERT_TRUE(buf.has_value());
245 EXPECT_STREQ(ret, buf.value().MakeString().c_str());
246 }
247
TEST_F(CXFA_FMParserTest,ParseFuncWithBadParamsList)248 TEST_F(CXFA_FMParserTest, ParseFuncWithBadParamsList) {
249 const wchar_t input[] =
250 LR"***(func MyFunction(param1,) do
251 param1 * param2
252 endfunc)***";
253
254 CXFA_FMLexer lexer(input);
255 CXFA_FMParser parser(heap(), &lexer);
256 CXFA_FMAST* ast = parser.Parse();
257 ASSERT_TRUE(ast == nullptr);
258 EXPECT_TRUE(parser.HasError());
259 }
260
TEST_F(CXFA_FMParserTest,ParseBadIfExpression)261 TEST_F(CXFA_FMParserTest, ParseBadIfExpression) {
262 const wchar_t input[] = L"if ( then";
263 CXFA_FMLexer lexer(input);
264 CXFA_FMParser parser(heap(), &lexer);
265 CXFA_FMAST* ast = parser.Parse();
266 ASSERT_TRUE(ast == nullptr);
267 EXPECT_TRUE(parser.HasError());
268 }
269
TEST_F(CXFA_FMParserTest,ParseBadElseIfExpression)270 TEST_F(CXFA_FMParserTest, ParseBadElseIfExpression) {
271 const wchar_t input[] =
272 LR"***(if ($ ne -1) then"
273 elseif( then)***";
274
275 CXFA_FMLexer lexer(input);
276 CXFA_FMParser parser(heap(), &lexer);
277 CXFA_FMAST* ast = parser.Parse();
278 ASSERT_TRUE(ast == nullptr);
279 EXPECT_TRUE(parser.HasError());
280 }
281
282 TEST_F(CXFA_FMParserTest, ParseDepthWithWideTree) {
283 const wchar_t input[] = L"a <> b <> c <> d <> e <> f <> g <> h <> i <> j";
284
285 {
286 CXFA_FMLexer lexer(input);
287 CXFA_FMParser parser(heap(), &lexer);
288 CXFA_FMAST* ast = parser.Parse();
289 ASSERT_TRUE(ast);
290 EXPECT_TRUE(!parser.HasError());
291 }
292
293 {
294 CXFA_FMLexer lexer(input);
295 CXFA_FMParser parser(heap(), &lexer);
296 parser.SetMaxParseDepthForTest(5);
297 CXFA_FMAST* ast = parser.Parse();
298 ASSERT_TRUE(ast == nullptr);
299 EXPECT_TRUE(parser.HasError());
300 }
301 }
302
303 TEST_F(CXFA_FMParserTest, ParseCallSmall) {
304 const wchar_t input[] = L"i.f(O)";
305 const wchar_t ret[] =
306 LR"***((function() {
307 let pfm_method_runner = function(obj, cb) {
308 if (pfm_rt.is_ary(obj)) {
309 let pfm_method_return = null;
310 for (var idx = obj.length -1; idx > 1; idx--) {
311 pfm_method_return = cb(obj[idx]);
312 }
313 return pfm_method_return;
314 }
315 return cb(obj);
316 };
317 var pfm_ret = null;
318 pfm_ret = pfm_rt.get_val((function() {
319 return pfm_method_runner(i, function(obj) {
320 return obj.f(pfm_rt.get_val(O));
321 });
322 }).call(this));
323 return pfm_rt.get_val(pfm_ret);
324 }).call(this);)***";
325
326 CXFA_FMLexer lexer(input);
327 CXFA_FMParser parser(heap(), &lexer);
328 CXFA_FMAST* ast = parser.Parse();
329 EXPECT_FALSE(parser.HasError());
330
331 CXFA_FMToJavaScriptDepth::Reset();
332 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
333 ASSERT_TRUE(buf.has_value());
334 EXPECT_STREQ(ret, buf.value().MakeString().c_str());
335 }
336
337 TEST_F(CXFA_FMParserTest, ParseCallBig) {
338 const wchar_t input[] = L"i.f(O.e(O.e(O)))";
339 const wchar_t ret[] =
340 LR"***((function() {
341 let pfm_method_runner = function(obj, cb) {
342 if (pfm_rt.is_ary(obj)) {
343 let pfm_method_return = null;
344 for (var idx = obj.length -1; idx > 1; idx--) {
345 pfm_method_return = cb(obj[idx]);
346 }
347 return pfm_method_return;
348 }
349 return cb(obj);
350 };
351 var pfm_ret = null;
352 pfm_ret = pfm_rt.get_val((function() {
353 return pfm_method_runner(i, function(obj) {
354 return obj.f(pfm_rt.get_val((function() {
355 return pfm_method_runner(O, function(obj) {
356 return obj.e(pfm_rt.get_val((function() {
357 return pfm_method_runner(O, function(obj) {
358 return obj.e(pfm_rt.get_val(O));
359 });
360 }).call(this)));
361 });
362 }).call(this)));
363 });
364 }).call(this));
365 return pfm_rt.get_val(pfm_ret);
366 }).call(this);)***";
367
368 CXFA_FMLexer lexer(input);
369 CXFA_FMParser parser(heap(), &lexer);
370 CXFA_FMAST* ast = parser.Parse();
371 EXPECT_FALSE(parser.HasError());
372
373 CXFA_FMToJavaScriptDepth::Reset();
374 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
375 ASSERT_TRUE(buf.has_value());
376 EXPECT_STREQ(ret, buf.value().MakeString().c_str());
377 }
378
379 TEST_F(CXFA_FMParserTest, ParseVar) {
380 const wchar_t input[] = LR"(var s = "")";
381 const wchar_t ret[] =
382 LR"***((function() {
383 let pfm_method_runner = function(obj, cb) {
384 if (pfm_rt.is_ary(obj)) {
385 let pfm_method_return = null;
386 for (var idx = obj.length -1; idx > 1; idx--) {
387 pfm_method_return = cb(obj[idx]);
388 }
389 return pfm_method_return;
390 }
391 return cb(obj);
392 };
393 var pfm_ret = null;
394 var s = "";
395 s = pfm_rt.var_filter(s);
396 pfm_ret = s;
397 return pfm_rt.get_val(pfm_ret);
398 }).call(this);)***";
399
400 CXFA_FMLexer lexer(input);
401 CXFA_FMParser parser(heap(), &lexer);
402 CXFA_FMAST* ast = parser.Parse();
403 EXPECT_FALSE(parser.HasError());
404
405 CXFA_FMToJavaScriptDepth::Reset();
406 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
407 ASSERT_TRUE(buf.has_value());
408 EXPECT_STREQ(ret, buf.value().MakeString().c_str());
409 }
410
411 TEST_F(CXFA_FMParserTest, ParseFunctionCallNoArguments) {
412 const wchar_t input[] = L"P.x()";
413 const wchar_t ret[] =
414 LR"***((function() {
415 let pfm_method_runner = function(obj, cb) {
416 if (pfm_rt.is_ary(obj)) {
417 let pfm_method_return = null;
418 for (var idx = obj.length -1; idx > 1; idx--) {
419 pfm_method_return = cb(obj[idx]);
420 }
421 return pfm_method_return;
422 }
423 return cb(obj);
424 };
425 var pfm_ret = null;
426 pfm_ret = pfm_rt.get_val((function() {
427 return pfm_method_runner(P, function(obj) {
428 return obj.x();
429 });
430 }).call(this));
431 return pfm_rt.get_val(pfm_ret);
432 }).call(this);)***";
433
434 CXFA_FMLexer lexer(input);
435 CXFA_FMParser parser(heap(), &lexer);
436 CXFA_FMAST* ast = parser.Parse();
437 EXPECT_FALSE(parser.HasError());
438
439 CXFA_FMToJavaScriptDepth::Reset();
440 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
441 ASSERT_TRUE(buf.has_value());
442 EXPECT_STREQ(ret, buf.value().MakeString().c_str());
443 }
444
445 TEST_F(CXFA_FMParserTest, ParseFunctionCallSingleArgument) {
446 const wchar_t input[] = L"P.x(foo)";
447 const wchar_t ret[] =
448 LR"***((function() {
449 let pfm_method_runner = function(obj, cb) {
450 if (pfm_rt.is_ary(obj)) {
451 let pfm_method_return = null;
452 for (var idx = obj.length -1; idx > 1; idx--) {
453 pfm_method_return = cb(obj[idx]);
454 }
455 return pfm_method_return;
456 }
457 return cb(obj);
458 };
459 var pfm_ret = null;
460 pfm_ret = pfm_rt.get_val((function() {
461 return pfm_method_runner(P, function(obj) {
462 return obj.x(pfm_rt.get_jsobj(foo));
463 });
464 }).call(this));
465 return pfm_rt.get_val(pfm_ret);
466 }).call(this);)***";
467
468 CXFA_FMLexer lexer(input);
469 CXFA_FMParser parser(heap(), &lexer);
470 CXFA_FMAST* ast = parser.Parse();
471 EXPECT_FALSE(parser.HasError());
472
473 CXFA_FMToJavaScriptDepth::Reset();
474 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
475 ASSERT_TRUE(buf.has_value());
476 EXPECT_STREQ(ret, buf.value().MakeString().c_str());
477 }
478
479 TEST_F(CXFA_FMParserTest, ParseFunctionCallMultipleArguments) {
480 const wchar_t input[] = L"P.x(foo, bar, baz)";
481 const wchar_t ret[] =
482 LR"***((function() {
483 let pfm_method_runner = function(obj, cb) {
484 if (pfm_rt.is_ary(obj)) {
485 let pfm_method_return = null;
486 for (var idx = obj.length -1; idx > 1; idx--) {
487 pfm_method_return = cb(obj[idx]);
488 }
489 return pfm_method_return;
490 }
491 return cb(obj);
492 };
493 var pfm_ret = null;
494 pfm_ret = pfm_rt.get_val((function() {
495 return pfm_method_runner(P, function(obj) {
496 return obj.x(pfm_rt.get_jsobj(foo), pfm_rt.get_val(bar), pfm_rt.get_val(baz));
497 });
498 }).call(this));
499 return pfm_rt.get_val(pfm_ret);
500 }).call(this);)***";
501
502 CXFA_FMLexer lexer(input);
503 CXFA_FMParser parser(heap(), &lexer);
504 CXFA_FMAST* ast = parser.Parse();
505 EXPECT_FALSE(parser.HasError());
506
507 CXFA_FMToJavaScriptDepth::Reset();
508 absl::optional<WideTextBuffer> buf = ast->ToJavaScript();
509 ASSERT_TRUE(buf.has_value());
510 EXPECT_STREQ(ret, buf.value().MakeString().c_str());
511 }
512
513 TEST_F(CXFA_FMParserTest, ParseFunctionCallMissingCommas) {
514 const wchar_t input[] = L"P.x(!foo!bar!baz)";
515 CXFA_FMLexer lexer(input);
516 CXFA_FMParser parser(heap(), &lexer);
517 CXFA_FMAST* ast = parser.Parse();
518 ASSERT_TRUE(ast == nullptr);
519 EXPECT_TRUE(parser.HasError());
520 }
521
522 TEST_F(CXFA_FMParserTest, ParseFunctionCallTrailingComma) {
523 const wchar_t input[] = L"P.x(foo,bar,baz,)";
524 CXFA_FMLexer lexer(input);
525 CXFA_FMParser parser(heap(), &lexer);
526 CXFA_FMAST* ast = parser.Parse();
527 ASSERT_TRUE(ast == nullptr);
528 EXPECT_TRUE(parser.HasError());
529 }
530
531 TEST_F(CXFA_FMParserTest, ParseFunctionCallExtraComma) {
532 const wchar_t input[] = L"P.x(foo,bar,,baz)";
533 CXFA_FMLexer lexer(input);
534 CXFA_FMParser parser(heap(), &lexer);
535 CXFA_FMAST* ast = parser.Parse();
536 ASSERT_TRUE(ast == nullptr);
537 EXPECT_TRUE(parser.HasError());
538 }
539