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