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