xref: /aosp_15_r20/external/pdfium/xfa/fxfa/formcalc/cxfa_fmparser_unittest.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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