• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "fxjs/cfxjse_formcalc_context.h"
8 
9 #include <time.h>
10 
11 #include <algorithm>
12 #include <string>
13 #include <utility>
14 
15 #include "core/fxcrt/cfx_decimal.h"
16 #include "core/fxcrt/cfx_widetextbuf.h"
17 #include "core/fxcrt/fx_extension.h"
18 #include "core/fxcrt/fx_random.h"
19 #include "fxjs/cfxjse_class.h"
20 #include "fxjs/cfxjse_engine.h"
21 #include "fxjs/cfxjse_value.h"
22 #include "fxjs/xfa/cjx_object.h"
23 #include "third_party/base/ptr_util.h"
24 #include "third_party/base/stl_util.h"
25 #include "xfa/fxfa/cxfa_ffnotify.h"
26 #include "xfa/fxfa/fm2js/cxfa_fmparser.h"
27 #include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h"
28 #include "xfa/fxfa/parser/cxfa_document.h"
29 #include "xfa/fxfa/parser/cxfa_localevalue.h"
30 #include "xfa/fxfa/parser/cxfa_node.h"
31 #include "xfa/fxfa/parser/cxfa_timezoneprovider.h"
32 #include "xfa/fxfa/parser/xfa_utils.h"
33 
34 namespace {
35 
36 const double kFinancialPrecision = 0.00000001;
37 
38 struct XFA_FMHtmlReserveCode {
39   uint32_t m_uCode;
40   const wchar_t* m_htmlReserve;
41 };
42 
43 // Sorted by m_htmlReserve
44 XFA_FMHtmlReserveCode reservesForDecode[] = {
45     {198, L"AElig"},   {193, L"Aacute"},   {194, L"Acirc"},
46     {192, L"Agrave"},  {913, L"Alpha"},    {197, L"Aring"},
47     {195, L"Atilde"},  {196, L"Auml"},     {914, L"Beta"},
48     {199, L"Ccedil"},  {935, L"Chi"},      {8225, L"Dagger"},
49     {916, L"Delta"},   {208, L"ETH"},      {201, L"Eacute"},
50     {202, L"Ecirc"},   {200, L"Egrave"},   {917, L"Epsilon"},
51     {919, L"Eta"},     {203, L"Euml"},     {915, L"Gamma"},
52     {922, L"Kappa"},   {923, L"Lambda"},   {924, L"Mu"},
53     {209, L"Ntilde"},  {925, L"Nu"},       {338, L"OElig"},
54     {211, L"Oacute"},  {212, L"Ocirc"},    {210, L"Ograve"},
55     {937, L"Omega"},   {927, L"Omicron"},  {216, L"Oslash"},
56     {213, L"Otilde"},  {214, L"Ouml"},     {934, L"Phi"},
57     {928, L"Pi"},      {936, L"Psi"},      {929, L"Rho"},
58     {352, L"Scaron"},  {931, L"Sigma"},    {222, L"THORN"},
59     {932, L"Tau"},     {920, L"Theta"},    {218, L"Uacute"},
60     {219, L"Ucirc"},   {217, L"Ugrave"},   {933, L"Upsilon"},
61     {220, L"Uuml"},    {926, L"Xi"},       {221, L"Yacute"},
62     {376, L"Yuml"},    {918, L"Zeta"},     {225, L"aacute"},
63     {226, L"acirc"},   {180, L"acute"},    {230, L"aelig"},
64     {224, L"agrave"},  {8501, L"alefsym"}, {945, L"alpha"},
65     {38, L"amp"},      {8743, L"and"},     {8736, L"ang"},
66     {39, L"apos"},     {229, L"aring"},    {8776, L"asymp"},
67     {227, L"atilde"},  {228, L"auml"},     {8222, L"bdquo"},
68     {946, L"beta"},    {166, L"brvbar"},   {8226, L"bull"},
69     {8745, L"cap"},    {231, L"ccedil"},   {184, L"cedil"},
70     {162, L"cent"},    {967, L"chi"},      {710, L"circ"},
71     {9827, L"clubs"},  {8773, L"cong"},    {169, L"copy"},
72     {8629, L"crarr"},  {8746, L"cup"},     {164, L"current"},
73     {8659, L"dArr"},   {8224, L"dagger"},  {8595, L"darr"},
74     {176, L"deg"},     {948, L"delta"},    {9830, L"diams"},
75     {247, L"divide"},  {233, L"eacute"},   {234, L"ecirc"},
76     {232, L"egrave"},  {8709, L"empty"},   {8195, L"emsp"},
77     {8194, L"ensp"},   {949, L"epsilon"},  {8801, L"equiv"},
78     {951, L"eta"},     {240, L"eth"},      {235, L"euml"},
79     {8364, L"euro"},   {8707, L"exist"},   {402, L"fnof"},
80     {8704, L"forall"}, {189, L"frac12"},   {188, L"frac14"},
81     {190, L"frac34"},  {8260, L"frasl"},   {947, L"gamma"},
82     {8805, L"ge"},     {62, L"gt"},        {8660, L"hArr"},
83     {8596, L"harr"},   {9829, L"hearts"},  {8230, L"hellip"},
84     {237, L"iacute"},  {238, L"icirc"},    {161, L"iexcl"},
85     {236, L"igrave"},  {8465, L"image"},   {8734, L"infin"},
86     {8747, L"int"},    {953, L"iota"},     {191, L"iquest"},
87     {8712, L"isin"},   {239, L"iuml"},     {954, L"kappa"},
88     {8656, L"lArr"},   {205, L"lacute"},   {955, L"lambda"},
89     {9001, L"lang"},   {171, L"laquo"},    {8592, L"larr"},
90     {8968, L"lceil"},  {206, L"lcirc"},    {8220, L"ldquo"},
91     {8804, L"le"},     {8970, L"lfloor"},  {204, L"lgrave"},
92     {921, L"lota"},    {8727, L"lowast"},  {9674, L"loz"},
93     {8206, L"lrm"},    {8249, L"lsaquo"},  {8216, L"lsquo"},
94     {60, L"lt"},       {207, L"luml"},     {175, L"macr"},
95     {8212, L"mdash"},  {181, L"micro"},    {183, L"middot"},
96     {8722, L"minus"},  {956, L"mu"},       {8711, L"nabla"},
97     {160, L"nbsp"},    {8211, L"ndash"},   {8800, L"ne"},
98     {8715, L"ni"},     {172, L"not"},      {8713, L"notin"},
99     {8836, L"nsub"},   {241, L"ntilde"},   {957, L"nu"},
100     {243, L"oacute"},  {244, L"ocirc"},    {339, L"oelig"},
101     {242, L"ograve"},  {8254, L"oline"},   {969, L"omega"},
102     {959, L"omicron"}, {8853, L"oplus"},   {8744, L"or"},
103     {170, L"ordf"},    {186, L"ordm"},     {248, L"oslash"},
104     {245, L"otilde"},  {8855, L"otimes"},  {246, L"ouml"},
105     {182, L"para"},    {8706, L"part"},    {8240, L"permil"},
106     {8869, L"perp"},   {966, L"phi"},      {960, L"pi"},
107     {982, L"piv"},     {177, L"plusmn"},   {8242, L"prime"},
108     {8719, L"prod"},   {8733, L"prop"},    {968, L"psi"},
109     {163, L"pund"},    {34, L"quot"},      {8658, L"rArr"},
110     {8730, L"radic"},  {9002, L"rang"},    {187, L"raquo"},
111     {8594, L"rarr"},   {8969, L"rceil"},   {8476, L"real"},
112     {174, L"reg"},     {8971, L"rfloor"},  {961, L"rho"},
113     {8207, L"rlm"},    {8250, L"rsaquo"},  {8217, L"rsquo"},
114     {353, L"saron"},   {8218, L"sbquo"},   {8901, L"sdot"},
115     {167, L"sect"},    {173, L"shy"},      {963, L"sigma"},
116     {962, L"sigmaf"},  {8764, L"sim"},     {9824, L"spades"},
117     {8834, L"sub"},    {8838, L"sube"},    {8721, L"sum"},
118     {8835, L"sup"},    {185, L"sup1"},     {178, L"sup2"},
119     {179, L"sup3"},    {8839, L"supe"},    {223, L"szlig"},
120     {964, L"tau"},     {8221, L"tdquo"},   {8756, L"there4"},
121     {952, L"theta"},   {977, L"thetasym"}, {8201, L"thinsp"},
122     {254, L"thorn"},   {732, L"tilde"},    {215, L"times"},
123     {8482, L"trade"},  {8657, L"uArr"},    {250, L"uacute"},
124     {8593, L"uarr"},   {251, L"ucirc"},    {249, L"ugrave"},
125     {168, L"uml"},     {978, L"upsih"},    {965, L"upsilon"},
126     {252, L"uuml"},    {8472, L"weierp"},  {958, L"xi"},
127     {253, L"yacute"},  {165, L"yen"},      {255, L"yuml"},
128     {950, L"zeta"},    {8205, L"zwj"},     {8204, L"zwnj"},
129 };
130 
131 // Sorted by m_uCode
132 const XFA_FMHtmlReserveCode reservesForEncode[] = {
133     {34, L"quot"},     {38, L"amp"},      {39, L"apos"},
134     {60, L"lt"},       {62, L"gt"},       {160, L"nbsp"},
135     {161, L"iexcl"},   {162, L"cent"},    {163, L"pund"},
136     {164, L"current"}, {165, L"yen"},     {166, L"brvbar"},
137     {167, L"sect"},    {168, L"uml"},     {169, L"copy"},
138     {170, L"ordf"},    {171, L"laquo"},   {172, L"not"},
139     {173, L"shy"},     {174, L"reg"},     {175, L"macr"},
140     {176, L"deg"},     {177, L"plusmn"},  {178, L"sup2"},
141     {179, L"sup3"},    {180, L"acute"},   {181, L"micro"},
142     {182, L"para"},    {183, L"middot"},  {184, L"cedil"},
143     {185, L"sup1"},    {186, L"ordm"},    {187, L"raquo"},
144     {188, L"frac14"},  {189, L"frac12"},  {190, L"frac34"},
145     {191, L"iquest"},  {192, L"Agrave"},  {193, L"Aacute"},
146     {194, L"Acirc"},   {195, L"Atilde"},  {196, L"Auml"},
147     {197, L"Aring"},   {198, L"AElig"},   {199, L"Ccedil"},
148     {200, L"Egrave"},  {201, L"Eacute"},  {202, L"Ecirc"},
149     {203, L"Euml"},    {204, L"lgrave"},  {205, L"lacute"},
150     {206, L"lcirc"},   {207, L"luml"},    {208, L"ETH"},
151     {209, L"Ntilde"},  {210, L"Ograve"},  {211, L"Oacute"},
152     {212, L"Ocirc"},   {213, L"Otilde"},  {214, L"Ouml"},
153     {215, L"times"},   {216, L"Oslash"},  {217, L"Ugrave"},
154     {218, L"Uacute"},  {219, L"Ucirc"},   {220, L"Uuml"},
155     {221, L"Yacute"},  {222, L"THORN"},   {223, L"szlig"},
156     {224, L"agrave"},  {225, L"aacute"},  {226, L"acirc"},
157     {227, L"atilde"},  {228, L"auml"},    {229, L"aring"},
158     {230, L"aelig"},   {231, L"ccedil"},  {232, L"egrave"},
159     {233, L"eacute"},  {234, L"ecirc"},   {235, L"euml"},
160     {236, L"igrave"},  {237, L"iacute"},  {238, L"icirc"},
161     {239, L"iuml"},    {240, L"eth"},     {241, L"ntilde"},
162     {242, L"ograve"},  {243, L"oacute"},  {244, L"ocirc"},
163     {245, L"otilde"},  {246, L"ouml"},    {247, L"divide"},
164     {248, L"oslash"},  {249, L"ugrave"},  {250, L"uacute"},
165     {251, L"ucirc"},   {252, L"uuml"},    {253, L"yacute"},
166     {254, L"thorn"},   {255, L"yuml"},    {338, L"OElig"},
167     {339, L"oelig"},   {352, L"Scaron"},  {353, L"saron"},
168     {376, L"Yuml"},    {402, L"fnof"},    {710, L"circ"},
169     {732, L"tilde"},   {913, L"Alpha"},   {914, L"Beta"},
170     {915, L"Gamma"},   {916, L"Delta"},   {917, L"Epsilon"},
171     {918, L"Zeta"},    {919, L"Eta"},     {920, L"Theta"},
172     {921, L"lota"},    {922, L"Kappa"},   {923, L"Lambda"},
173     {924, L"Mu"},      {925, L"Nu"},      {926, L"Xi"},
174     {927, L"Omicron"}, {928, L"Pi"},      {929, L"Rho"},
175     {931, L"Sigma"},   {932, L"Tau"},     {933, L"Upsilon"},
176     {934, L"Phi"},     {935, L"Chi"},     {936, L"Psi"},
177     {937, L"Omega"},   {945, L"alpha"},   {946, L"beta"},
178     {947, L"gamma"},   {948, L"delta"},   {949, L"epsilon"},
179     {950, L"zeta"},    {951, L"eta"},     {952, L"theta"},
180     {953, L"iota"},    {954, L"kappa"},   {955, L"lambda"},
181     {956, L"mu"},      {957, L"nu"},      {958, L"xi"},
182     {959, L"omicron"}, {960, L"pi"},      {961, L"rho"},
183     {962, L"sigmaf"},  {963, L"sigma"},   {964, L"tau"},
184     {965, L"upsilon"}, {966, L"phi"},     {967, L"chi"},
185     {968, L"psi"},     {969, L"omega"},   {977, L"thetasym"},
186     {978, L"upsih"},   {982, L"piv"},     {8194, L"ensp"},
187     {8195, L"emsp"},   {8201, L"thinsp"}, {8204, L"zwnj"},
188     {8205, L"zwj"},    {8206, L"lrm"},    {8207, L"rlm"},
189     {8211, L"ndash"},  {8212, L"mdash"},  {8216, L"lsquo"},
190     {8217, L"rsquo"},  {8218, L"sbquo"},  {8220, L"ldquo"},
191     {8221, L"tdquo"},  {8222, L"bdquo"},  {8224, L"dagger"},
192     {8225, L"Dagger"}, {8226, L"bull"},   {8230, L"hellip"},
193     {8240, L"permil"}, {8242, L"prime"},  {8249, L"lsaquo"},
194     {8250, L"rsaquo"}, {8254, L"oline"},  {8260, L"frasl"},
195     {8364, L"euro"},   {8465, L"image"},  {8472, L"weierp"},
196     {8476, L"real"},   {8482, L"trade"},  {8501, L"alefsym"},
197     {8592, L"larr"},   {8593, L"uarr"},   {8594, L"rarr"},
198     {8595, L"darr"},   {8596, L"harr"},   {8629, L"crarr"},
199     {8656, L"lArr"},   {8657, L"uArr"},   {8658, L"rArr"},
200     {8659, L"dArr"},   {8660, L"hArr"},   {8704, L"forall"},
201     {8706, L"part"},   {8707, L"exist"},  {8709, L"empty"},
202     {8711, L"nabla"},  {8712, L"isin"},   {8713, L"notin"},
203     {8715, L"ni"},     {8719, L"prod"},   {8721, L"sum"},
204     {8722, L"minus"},  {8727, L"lowast"}, {8730, L"radic"},
205     {8733, L"prop"},   {8734, L"infin"},  {8736, L"ang"},
206     {8743, L"and"},    {8744, L"or"},     {8745, L"cap"},
207     {8746, L"cup"},    {8747, L"int"},    {8756, L"there4"},
208     {8764, L"sim"},    {8773, L"cong"},   {8776, L"asymp"},
209     {8800, L"ne"},     {8801, L"equiv"},  {8804, L"le"},
210     {8805, L"ge"},     {8834, L"sub"},    {8835, L"sup"},
211     {8836, L"nsub"},   {8838, L"sube"},   {8839, L"supe"},
212     {8853, L"oplus"},  {8855, L"otimes"}, {8869, L"perp"},
213     {8901, L"sdot"},   {8968, L"lceil"},  {8969, L"rceil"},
214     {8970, L"lfloor"}, {8971, L"rfloor"}, {9001, L"lang"},
215     {9002, L"rang"},   {9674, L"loz"},    {9824, L"spades"},
216     {9827, L"clubs"},  {9829, L"hearts"}, {9830, L"diams"},
217 };
218 
219 const FXJSE_FUNCTION_DESCRIPTOR formcalc_fm2js_functions[] = {
220     {"Abs", CFXJSE_FormCalcContext::Abs},
221     {"Avg", CFXJSE_FormCalcContext::Avg},
222     {"Ceil", CFXJSE_FormCalcContext::Ceil},
223     {"Count", CFXJSE_FormCalcContext::Count},
224     {"Floor", CFXJSE_FormCalcContext::Floor},
225     {"Max", CFXJSE_FormCalcContext::Max},
226     {"Min", CFXJSE_FormCalcContext::Min},
227     {"Mod", CFXJSE_FormCalcContext::Mod},
228     {"Round", CFXJSE_FormCalcContext::Round},
229     {"Sum", CFXJSE_FormCalcContext::Sum},
230     {"Date", CFXJSE_FormCalcContext::Date},
231     {"Date2Num", CFXJSE_FormCalcContext::Date2Num},
232     {"DateFmt", CFXJSE_FormCalcContext::DateFmt},
233     {"IsoDate2Num", CFXJSE_FormCalcContext::IsoDate2Num},
234     {"IsoTime2Num", CFXJSE_FormCalcContext::IsoTime2Num},
235     {"LocalDateFmt", CFXJSE_FormCalcContext::LocalDateFmt},
236     {"LocalTimeFmt", CFXJSE_FormCalcContext::LocalTimeFmt},
237     {"Num2Date", CFXJSE_FormCalcContext::Num2Date},
238     {"Num2GMTime", CFXJSE_FormCalcContext::Num2GMTime},
239     {"Num2Time", CFXJSE_FormCalcContext::Num2Time},
240     {"Time", CFXJSE_FormCalcContext::Time},
241     {"Time2Num", CFXJSE_FormCalcContext::Time2Num},
242     {"TimeFmt", CFXJSE_FormCalcContext::TimeFmt},
243     {"Apr", CFXJSE_FormCalcContext::Apr},
244     {"Cterm", CFXJSE_FormCalcContext::CTerm},
245     {"FV", CFXJSE_FormCalcContext::FV},
246     {"Ipmt", CFXJSE_FormCalcContext::IPmt},
247     {"NPV", CFXJSE_FormCalcContext::NPV},
248     {"Pmt", CFXJSE_FormCalcContext::Pmt},
249     {"PPmt", CFXJSE_FormCalcContext::PPmt},
250     {"PV", CFXJSE_FormCalcContext::PV},
251     {"Rate", CFXJSE_FormCalcContext::Rate},
252     {"Term", CFXJSE_FormCalcContext::Term},
253     {"Choose", CFXJSE_FormCalcContext::Choose},
254     {"Exists", CFXJSE_FormCalcContext::Exists},
255     {"HasValue", CFXJSE_FormCalcContext::HasValue},
256     {"Oneof", CFXJSE_FormCalcContext::Oneof},
257     {"Within", CFXJSE_FormCalcContext::Within},
258     {"If", CFXJSE_FormCalcContext::If},
259     {"Eval", CFXJSE_FormCalcContext::Eval},
260     {"Translate", CFXJSE_FormCalcContext::eval_translation},
261     {"Ref", CFXJSE_FormCalcContext::Ref},
262     {"UnitType", CFXJSE_FormCalcContext::UnitType},
263     {"UnitValue", CFXJSE_FormCalcContext::UnitValue},
264     {"At", CFXJSE_FormCalcContext::At},
265     {"Concat", CFXJSE_FormCalcContext::Concat},
266     {"Decode", CFXJSE_FormCalcContext::Decode},
267     {"Encode", CFXJSE_FormCalcContext::Encode},
268     {"Format", CFXJSE_FormCalcContext::Format},
269     {"Left", CFXJSE_FormCalcContext::Left},
270     {"Len", CFXJSE_FormCalcContext::Len},
271     {"Lower", CFXJSE_FormCalcContext::Lower},
272     {"Ltrim", CFXJSE_FormCalcContext::Ltrim},
273     {"Parse", CFXJSE_FormCalcContext::Parse},
274     {"Replace", CFXJSE_FormCalcContext::Replace},
275     {"Right", CFXJSE_FormCalcContext::Right},
276     {"Rtrim", CFXJSE_FormCalcContext::Rtrim},
277     {"Space", CFXJSE_FormCalcContext::Space},
278     {"Str", CFXJSE_FormCalcContext::Str},
279     {"Stuff", CFXJSE_FormCalcContext::Stuff},
280     {"Substr", CFXJSE_FormCalcContext::Substr},
281     {"Uuid", CFXJSE_FormCalcContext::Uuid},
282     {"Upper", CFXJSE_FormCalcContext::Upper},
283     {"WordNum", CFXJSE_FormCalcContext::WordNum},
284     {"Get", CFXJSE_FormCalcContext::Get},
285     {"Post", CFXJSE_FormCalcContext::Post},
286     {"Put", CFXJSE_FormCalcContext::Put},
287     {"pos_op", CFXJSE_FormCalcContext::positive_operator},
288     {"neg_op", CFXJSE_FormCalcContext::negative_operator},
289     {"log_or_op", CFXJSE_FormCalcContext::logical_or_operator},
290     {"log_and_op", CFXJSE_FormCalcContext::logical_and_operator},
291     {"log_not_op", CFXJSE_FormCalcContext::logical_not_operator},
292     {"eq_op", CFXJSE_FormCalcContext::equality_operator},
293     {"neq_op", CFXJSE_FormCalcContext::notequality_operator},
294     {"lt_op", CFXJSE_FormCalcContext::less_operator},
295     {"le_op", CFXJSE_FormCalcContext::lessequal_operator},
296     {"gt_op", CFXJSE_FormCalcContext::greater_operator},
297     {"ge_op", CFXJSE_FormCalcContext::greaterequal_operator},
298     {"plus_op", CFXJSE_FormCalcContext::plus_operator},
299     {"minus_op", CFXJSE_FormCalcContext::minus_operator},
300     {"mul_op", CFXJSE_FormCalcContext::multiple_operator},
301     {"div_op", CFXJSE_FormCalcContext::divide_operator},
302     {"asgn_val_op", CFXJSE_FormCalcContext::assign_value_operator},
303     {"dot_acc", CFXJSE_FormCalcContext::dot_accessor},
304     {"dotdot_acc", CFXJSE_FormCalcContext::dotdot_accessor},
305     {"concat_obj", CFXJSE_FormCalcContext::concat_fm_object},
306     {"is_obj", CFXJSE_FormCalcContext::is_fm_object},
307     {"is_ary", CFXJSE_FormCalcContext::is_fm_array},
308     {"get_val", CFXJSE_FormCalcContext::get_fm_value},
309     {"get_jsobj", CFXJSE_FormCalcContext::get_fm_jsobj},
310     {"var_filter", CFXJSE_FormCalcContext::fm_var_filter},
311 };
312 
313 const FXJSE_CLASS_DESCRIPTOR formcalc_fm2js_descriptor = {
314     "XFA_FM2JS_FormCalcClass",               // name
315     formcalc_fm2js_functions,                // methods
316     FX_ArraySize(formcalc_fm2js_functions),  // number of methods
317     nullptr,                                 // dynamic prop type
318     nullptr,                                 // dynamic prop getter
319     nullptr,                                 // dynamic prop setter
320     nullptr,                                 // dynamic prop method call
321 };
322 
323 const uint8_t g_sAltTable_Date[] = {
324     255, 255, 255, 3,   9,   255, 255, 255, 255, 255, 255,
325     255, 2,   255, 255, 255, 255, 255, 255, 255, 255, 255,
326     255, 255, 1,   255, 255, 255, 255, 255, 255, 255, 255,
327 };
328 static_assert(FX_ArraySize(g_sAltTable_Date) == L'a' - L'A' + 1,
329               "Invalid g_sAltTable_Date size.");
330 
331 const uint8_t g_sAltTable_Time[] = {
332     14,  255, 255, 3,   9,   255, 255, 15,  255, 255, 255,
333     255, 6,   255, 255, 255, 255, 255, 7,   255, 255, 255,
334     255, 255, 1,   17,  255, 255, 255, 255, 255, 255, 255,
335 };
336 static_assert(FX_ArraySize(g_sAltTable_Time) == L'a' - L'A' + 1,
337               "Invalid g_sAltTable_Time size.");
338 
AlternateDateTimeSymbols(WideString & wsPattern,const WideString & wsAltSymbols,const uint8_t * pAltTable)339 void AlternateDateTimeSymbols(WideString& wsPattern,
340                               const WideString& wsAltSymbols,
341                               const uint8_t* pAltTable) {
342   int32_t nLength = wsPattern.GetLength();
343   bool bInConstRange = false;
344   bool bEscape = false;
345   int32_t i = 0;
346   while (i < nLength) {
347     wchar_t wc = wsPattern[i];
348     if (wc == L'\'') {
349       bInConstRange = !bInConstRange;
350       if (bEscape) {
351         i++;
352       } else {
353         wsPattern.Delete(i);
354         nLength--;
355       }
356       bEscape = !bEscape;
357       continue;
358     }
359     if (!bInConstRange && wc >= L'A' && wc <= L'a') {
360       uint8_t nAlt = pAltTable[wc - L'A'];
361       if (nAlt != 255)
362         wsPattern.SetAt(i, wsAltSymbols[nAlt]);
363     }
364     i++;
365     bEscape = false;
366   }
367 }
368 
PatternStringType(const ByteStringView & szPattern,uint32_t & patternType)369 bool PatternStringType(const ByteStringView& szPattern, uint32_t& patternType) {
370   WideString wsPattern = WideString::FromUTF8(szPattern);
371   if (L"datetime" == wsPattern.Left(8)) {
372     patternType = XFA_VT_DATETIME;
373     return true;
374   }
375   if (L"date" == wsPattern.Left(4)) {
376     auto pos = wsPattern.Find(L"time");
377     patternType =
378         pos.has_value() && pos.value() != 0 ? XFA_VT_DATETIME : XFA_VT_DATE;
379     return true;
380   }
381   if (L"time" == wsPattern.Left(4)) {
382     patternType = XFA_VT_TIME;
383     return true;
384   }
385   if (L"text" == wsPattern.Left(4)) {
386     patternType = XFA_VT_TEXT;
387     return true;
388   }
389   if (L"num" == wsPattern.Left(3)) {
390     if (L"integer" == wsPattern.Mid(4, 7)) {
391       patternType = XFA_VT_INTEGER;
392     } else if (L"decimal" == wsPattern.Mid(4, 7)) {
393       patternType = XFA_VT_DECIMAL;
394     } else if (L"currency" == wsPattern.Mid(4, 8)) {
395       patternType = XFA_VT_FLOAT;
396     } else if (L"percent" == wsPattern.Mid(4, 7)) {
397       patternType = XFA_VT_FLOAT;
398     } else {
399       patternType = XFA_VT_FLOAT;
400     }
401     return true;
402   }
403 
404   patternType = XFA_VT_NULL;
405   wsPattern.MakeLower();
406   const wchar_t* pData = wsPattern.c_str();
407   int32_t iLength = wsPattern.GetLength();
408   int32_t iIndex = 0;
409   bool bSingleQuotation = false;
410   wchar_t patternChar;
411   while (iIndex < iLength) {
412     patternChar = pData[iIndex];
413     if (patternChar == 0x27) {
414       bSingleQuotation = !bSingleQuotation;
415     } else if (!bSingleQuotation &&
416                (patternChar == 'y' || patternChar == 'j')) {
417       patternType = XFA_VT_DATE;
418       iIndex++;
419       wchar_t timePatternChar;
420       while (iIndex < iLength) {
421         timePatternChar = pData[iIndex];
422         if (timePatternChar == 0x27) {
423           bSingleQuotation = !bSingleQuotation;
424         } else if (!bSingleQuotation && timePatternChar == 't') {
425           patternType = XFA_VT_DATETIME;
426           break;
427         }
428         iIndex++;
429       }
430       break;
431     } else if (!bSingleQuotation &&
432                (patternChar == 'h' || patternChar == 'k')) {
433       patternType = XFA_VT_TIME;
434       break;
435     } else if (!bSingleQuotation &&
436                (patternChar == 'a' || patternChar == 'x' ||
437                 patternChar == 'o' || patternChar == '0')) {
438       patternType = XFA_VT_TEXT;
439       if (patternChar == 'x' || patternChar == 'o' || patternChar == '0') {
440         break;
441       }
442     } else if (!bSingleQuotation &&
443                (patternChar == 'z' || patternChar == 's' ||
444                 patternChar == 'e' || patternChar == 'v' ||
445                 patternChar == '8' || patternChar == ',' ||
446                 patternChar == '.' || patternChar == '$')) {
447       patternType = XFA_VT_FLOAT;
448       if (patternChar == 'v' || patternChar == '8' || patternChar == '$') {
449         break;
450       }
451     }
452     iIndex++;
453   }
454   if (patternType == XFA_VT_NULL) {
455     patternType = XFA_VT_TEXT | XFA_VT_FLOAT;
456   }
457   return false;
458 }
459 
ToJSContext(CFXJSE_Value * pValue,CFXJSE_Class * pClass)460 CFXJSE_FormCalcContext* ToJSContext(CFXJSE_Value* pValue,
461                                     CFXJSE_Class* pClass) {
462   CFXJSE_HostObject* pHostObj = pValue->ToHostObject(pClass);
463   if (!pHostObj || pHostObj->type() != CFXJSE_HostObject::kFM2JS)
464     return nullptr;
465   return static_cast<CFXJSE_FormCalcContext*>(pHostObj);
466 }
467 
IsWhitespace(char c)468 bool IsWhitespace(char c) {
469   return c == 0x20 || c == 0x09 || c == 0x0B || c == 0x0C || c == 0x0A ||
470          c == 0x0D;
471 }
472 
LocaleFromString(CXFA_Document * pDoc,CXFA_LocaleMgr * pMgr,const ByteStringView & szLocale)473 IFX_Locale* LocaleFromString(CXFA_Document* pDoc,
474                              CXFA_LocaleMgr* pMgr,
475                              const ByteStringView& szLocale) {
476   if (!szLocale.IsEmpty())
477     return pMgr->GetLocaleByName(WideString::FromUTF8(szLocale));
478 
479   CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
480   ASSERT(pThisNode);
481   return pThisNode->GetLocale();
482 }
483 
FormatFromString(IFX_Locale * pLocale,const ByteStringView & szFormat)484 WideString FormatFromString(IFX_Locale* pLocale,
485                             const ByteStringView& szFormat) {
486   if (!szFormat.IsEmpty())
487     return WideString::FromUTF8(szFormat);
488 
489   return pLocale->GetDatePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
490 }
491 
SubCategoryFromInt(int32_t iStyle)492 FX_LOCALEDATETIMESUBCATEGORY SubCategoryFromInt(int32_t iStyle) {
493   switch (iStyle) {
494     case 1:
495       return FX_LOCALEDATETIMESUBCATEGORY_Short;
496     case 3:
497       return FX_LOCALEDATETIMESUBCATEGORY_Long;
498     case 4:
499       return FX_LOCALEDATETIMESUBCATEGORY_Full;
500     case 0:
501     case 2:
502     default:
503       return FX_LOCALEDATETIMESUBCATEGORY_Medium;
504   }
505 }
506 
IsPartOfNumber(char ch)507 bool IsPartOfNumber(char ch) {
508   return std::isdigit(ch) || ch == '-' || ch == '.';
509 }
510 
IsPartOfNumberW(wchar_t ch)511 bool IsPartOfNumberW(wchar_t ch) {
512   return std::iswdigit(ch) || ch == L'-' || ch == L'.';
513 }
514 
GUIDString(bool bSeparator)515 ByteString GUIDString(bool bSeparator) {
516   uint8_t data[16];
517   FX_Random_GenerateMT(reinterpret_cast<uint32_t*>(data), 4);
518   data[6] = (data[6] & 0x0F) | 0x40;
519 
520   ByteString bsStr;
521   char* pBuf = bsStr.GetBuffer(40);
522   for (int32_t i = 0; i < 16; ++i, pBuf += 2) {
523     if (bSeparator && (i == 4 || i == 6 || i == 8 || i == 10))
524       *pBuf++ = L'-';
525 
526     FXSYS_IntToTwoHexChars(data[i], pBuf);
527   }
528   bsStr.ReleaseBuffer(bSeparator ? 36 : 32);
529   return bsStr;
530 }
531 
ByteStringToDouble(const ByteStringView & szStringVal)532 double ByteStringToDouble(const ByteStringView& szStringVal) {
533   WideString wsValue = WideString::FromUTF8(szStringVal);
534   wsValue.Trim();
535 
536   int32_t cc = 0;
537   bool bNegative = false;
538 
539   const wchar_t* str = wsValue.c_str();
540   int32_t len = wsValue.GetLength();
541   if (str[0] == '+') {
542     cc++;
543   } else if (str[0] == '-') {
544     bNegative = true;
545     cc++;
546   }
547 
548   int32_t nIntegralLen = 0;
549   int64_t nIntegral = 0;
550   while (cc < len) {
551     if (str[cc] == '.' || str[cc] == 'E' || str[cc] == 'e' ||
552         nIntegralLen > 17) {
553       break;
554     }
555     if (!FXSYS_isDecimalDigit(str[cc])) {
556       return 0;
557     }
558     nIntegral = nIntegral * 10 + str[cc] - '0';
559     cc++;
560     nIntegralLen++;
561   }
562   nIntegral = bNegative ? -nIntegral : nIntegral;
563 
564   int32_t scale = 0;
565   double fraction = 0.0;
566   uint32_t dwFractional = 0;
567   if (cc < len && str[cc] == '.') {
568     cc++;
569     while (cc < len) {
570       fraction += XFA_GetFractionalScale(scale) * (str[cc] - '0');
571       scale++;
572       cc++;
573       if (cc == len)
574         break;
575       if (scale == XFA_GetMaxFractionalScale() || str[cc] == 'E' ||
576           str[cc] == 'e') {
577         break;
578       }
579       if (!FXSYS_isDecimalDigit(str[cc]))
580         return 0;
581     }
582     dwFractional = static_cast<uint32_t>(fraction * 4294967296.0);
583   }
584 
585   int32_t nExponent = 0;
586   bool bExpSign = false;
587   if (cc < len && (str[cc] == 'E' || str[cc] == 'e')) {
588     cc++;
589     if (cc < len) {
590       if (str[cc] == '+') {
591         cc++;
592       } else if (str[cc] == '-') {
593         bExpSign = true;
594         cc++;
595       }
596     }
597     while (cc < len) {
598       if (str[cc] == '.' || !FXSYS_isDecimalDigit(str[cc]))
599         return 0;
600 
601       nExponent = nExponent * 10 + str[cc] - '0';
602       cc++;
603     }
604     nExponent = bExpSign ? -nExponent : nExponent;
605   }
606 
607   double dValue = dwFractional / 4294967296.0;
608   dValue = nIntegral + (nIntegral >= 0 ? dValue : -dValue);
609   if (nExponent != 0)
610     dValue *= FXSYS_pow(10, static_cast<float>(nExponent));
611 
612   return dValue;
613 }
614 
615 }  // namespace
616 
617 // static
Abs(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)618 void CFXJSE_FormCalcContext::Abs(CFXJSE_Value* pThis,
619                                  const ByteStringView& szFuncName,
620                                  CFXJSE_Arguments& args) {
621   if (args.GetLength() != 1) {
622     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Abs");
623     return;
624   }
625 
626   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
627   if (ValueIsNull(pThis, argOne.get())) {
628     args.GetReturnValue()->SetNull();
629     return;
630   }
631 
632   double dValue = ValueToDouble(pThis, argOne.get());
633   if (dValue < 0)
634     dValue = -dValue;
635 
636   args.GetReturnValue()->SetDouble(dValue);
637 }
638 
639 // static
Avg(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)640 void CFXJSE_FormCalcContext::Avg(CFXJSE_Value* pThis,
641                                  const ByteStringView& szFuncName,
642                                  CFXJSE_Arguments& args) {
643   int32_t argc = args.GetLength();
644   if (argc < 1) {
645     args.GetReturnValue()->SetNull();
646     return;
647   }
648 
649   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
650   uint32_t uCount = 0;
651   double dSum = 0.0;
652   for (int32_t i = 0; i < argc; i++) {
653     std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
654     if (argValue->IsNull())
655       continue;
656 
657     if (!argValue->IsArray()) {
658       dSum += ValueToDouble(pThis, argValue.get());
659       uCount++;
660       continue;
661     }
662 
663     auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
664     argValue->GetObjectProperty("length", lengthValue.get());
665     int32_t iLength = lengthValue->ToInteger();
666 
667     if (iLength > 2) {
668       auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
669       argValue->GetObjectPropertyByIdx(1, propertyValue.get());
670 
671       auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
672       if (propertyValue->IsNull()) {
673         for (int32_t j = 2; j < iLength; j++) {
674           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
675           auto defaultPropValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
676           GetObjectDefaultValue(jsObjectValue.get(), defaultPropValue.get());
677           if (defaultPropValue->IsNull())
678             continue;
679 
680           dSum += ValueToDouble(pThis, defaultPropValue.get());
681           uCount++;
682         }
683       } else {
684         for (int32_t j = 2; j < iLength; j++) {
685           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
686           auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
687           jsObjectValue->GetObjectProperty(
688               propertyValue->ToString().AsStringView(), newPropertyValue.get());
689           if (newPropertyValue->IsNull())
690             continue;
691 
692           dSum += ValueToDouble(pThis, newPropertyValue.get());
693           uCount++;
694         }
695       }
696     }
697   }
698   if (uCount == 0) {
699     args.GetReturnValue()->SetNull();
700     return;
701   }
702 
703   args.GetReturnValue()->SetDouble(dSum / uCount);
704 }
705 
706 // static
Ceil(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)707 void CFXJSE_FormCalcContext::Ceil(CFXJSE_Value* pThis,
708                                   const ByteStringView& szFuncName,
709                                   CFXJSE_Arguments& args) {
710   if (args.GetLength() != 1) {
711     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Ceil");
712     return;
713   }
714 
715   std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
716   if (ValueIsNull(pThis, argValue.get())) {
717     args.GetReturnValue()->SetNull();
718     return;
719   }
720 
721   args.GetReturnValue()->SetFloat(ceil(ValueToFloat(pThis, argValue.get())));
722 }
723 
724 // static
Count(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)725 void CFXJSE_FormCalcContext::Count(CFXJSE_Value* pThis,
726                                    const ByteStringView& szFuncName,
727                                    CFXJSE_Arguments& args) {
728   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
729   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
730   int32_t iCount = 0;
731   for (int32_t i = 0; i < args.GetLength(); i++) {
732     std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
733     if (argValue->IsNull())
734       continue;
735 
736     if (argValue->IsArray()) {
737       auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
738       argValue->GetObjectProperty("length", lengthValue.get());
739 
740       int32_t iLength = lengthValue->ToInteger();
741       if (iLength <= 2) {
742         pContext->ThrowArgumentMismatchException();
743         return;
744       }
745 
746       auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
747       auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
748       auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
749       argValue->GetObjectPropertyByIdx(1, propertyValue.get());
750       argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
751       if (propertyValue->IsNull()) {
752         for (int32_t j = 2; j < iLength; j++) {
753           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
754           GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
755           if (!newPropertyValue->IsNull())
756             iCount++;
757         }
758       } else {
759         for (int32_t j = 2; j < iLength; j++) {
760           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
761           jsObjectValue->GetObjectProperty(
762               propertyValue->ToString().AsStringView(), newPropertyValue.get());
763           iCount += newPropertyValue->IsNull() ? 0 : 1;
764         }
765       }
766     } else if (argValue->IsObject()) {
767       auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
768       GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
769       if (!newPropertyValue->IsNull())
770         iCount++;
771     } else {
772       iCount++;
773     }
774   }
775   args.GetReturnValue()->SetInteger(iCount);
776 }
777 
778 // static
Floor(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)779 void CFXJSE_FormCalcContext::Floor(CFXJSE_Value* pThis,
780                                    const ByteStringView& szFuncName,
781                                    CFXJSE_Arguments& args) {
782   if (args.GetLength() != 1) {
783     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Floor");
784     return;
785   }
786 
787   std::unique_ptr<CFXJSE_Value> argValue = GetSimpleValue(pThis, args, 0);
788   if (ValueIsNull(pThis, argValue.get())) {
789     args.GetReturnValue()->SetNull();
790     return;
791   }
792 
793   args.GetReturnValue()->SetFloat(floor(ValueToFloat(pThis, argValue.get())));
794 }
795 
796 // static
Max(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)797 void CFXJSE_FormCalcContext::Max(CFXJSE_Value* pThis,
798                                  const ByteStringView& szFuncName,
799                                  CFXJSE_Arguments& args) {
800   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
801   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
802   uint32_t uCount = 0;
803   double dMaxValue = 0.0;
804   for (int32_t i = 0; i < args.GetLength(); i++) {
805     std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
806     if (argValue->IsNull())
807       continue;
808 
809     if (argValue->IsArray()) {
810       auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
811       argValue->GetObjectProperty("length", lengthValue.get());
812       int32_t iLength = lengthValue->ToInteger();
813       if (iLength <= 2) {
814         pContext->ThrowArgumentMismatchException();
815         return;
816       }
817 
818       auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
819       auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
820       auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
821       argValue->GetObjectPropertyByIdx(1, propertyValue.get());
822       argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
823       if (propertyValue->IsNull()) {
824         for (int32_t j = 2; j < iLength; j++) {
825           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
826           GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
827           if (newPropertyValue->IsNull())
828             continue;
829 
830           uCount++;
831           double dValue = ValueToDouble(pThis, newPropertyValue.get());
832           dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
833         }
834       } else {
835         for (int32_t j = 2; j < iLength; j++) {
836           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
837           jsObjectValue->GetObjectProperty(
838               propertyValue->ToString().AsStringView(), newPropertyValue.get());
839           if (newPropertyValue->IsNull())
840             continue;
841 
842           uCount++;
843           double dValue = ValueToDouble(pThis, newPropertyValue.get());
844           dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
845         }
846       }
847     } else if (argValue->IsObject()) {
848       auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
849       GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
850       if (newPropertyValue->IsNull())
851         continue;
852 
853       uCount++;
854       double dValue = ValueToDouble(pThis, newPropertyValue.get());
855       dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
856     } else {
857       uCount++;
858       double dValue = ValueToDouble(pThis, argValue.get());
859       dMaxValue = (uCount == 1) ? dValue : std::max(dMaxValue, dValue);
860     }
861   }
862   if (uCount == 0) {
863     args.GetReturnValue()->SetNull();
864     return;
865   }
866 
867   args.GetReturnValue()->SetDouble(dMaxValue);
868 }
869 
870 // static
Min(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)871 void CFXJSE_FormCalcContext::Min(CFXJSE_Value* pThis,
872                                  const ByteStringView& szFuncName,
873                                  CFXJSE_Arguments& args) {
874   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
875   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
876   uint32_t uCount = 0;
877   double dMinValue = 0.0;
878   for (int32_t i = 0; i < args.GetLength(); i++) {
879     std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
880     if (argValue->IsNull())
881       continue;
882 
883     if (argValue->IsArray()) {
884       auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
885       argValue->GetObjectProperty("length", lengthValue.get());
886       int32_t iLength = lengthValue->ToInteger();
887       if (iLength <= 2) {
888         pContext->ThrowArgumentMismatchException();
889         return;
890       }
891 
892       auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
893       auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
894       auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
895       argValue->GetObjectPropertyByIdx(1, propertyValue.get());
896       argValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
897       if (propertyValue->IsNull()) {
898         for (int32_t j = 2; j < iLength; j++) {
899           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
900           GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
901           if (newPropertyValue->IsNull())
902             continue;
903 
904           uCount++;
905           double dValue = ValueToDouble(pThis, newPropertyValue.get());
906           dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
907         }
908       } else {
909         for (int32_t j = 2; j < iLength; j++) {
910           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
911           jsObjectValue->GetObjectProperty(
912               propertyValue->ToString().AsStringView(), newPropertyValue.get());
913           if (newPropertyValue->IsNull())
914             continue;
915 
916           uCount++;
917           double dValue = ValueToDouble(pThis, newPropertyValue.get());
918           dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
919         }
920       }
921     } else if (argValue->IsObject()) {
922       auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
923       GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
924       if (newPropertyValue->IsNull())
925         continue;
926 
927       uCount++;
928       double dValue = ValueToDouble(pThis, newPropertyValue.get());
929       dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
930     } else {
931       uCount++;
932       double dValue = ValueToDouble(pThis, argValue.get());
933       dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue);
934     }
935   }
936   if (uCount == 0) {
937     args.GetReturnValue()->SetNull();
938     return;
939   }
940 
941   args.GetReturnValue()->SetDouble(dMinValue);
942 }
943 
944 // static
Mod(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)945 void CFXJSE_FormCalcContext::Mod(CFXJSE_Value* pThis,
946                                  const ByteStringView& szFuncName,
947                                  CFXJSE_Arguments& args) {
948   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
949   if (args.GetLength() != 2) {
950     pContext->ThrowParamCountMismatchException(L"Mod");
951     return;
952   }
953 
954   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
955   std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
956   if (argOne->IsNull() || argTwo->IsNull()) {
957     args.GetReturnValue()->SetNull();
958     return;
959   }
960 
961   bool argOneResult;
962   double dDividend = ExtractDouble(pThis, argOne.get(), &argOneResult);
963   bool argTwoResult;
964   double dDivisor = ExtractDouble(pThis, argTwo.get(), &argTwoResult);
965   if (!argOneResult || !argTwoResult) {
966     pContext->ThrowArgumentMismatchException();
967     return;
968   }
969 
970   if (dDivisor == 0.0) {
971     pContext->ThrowDivideByZeroException();
972     return;
973   }
974 
975   args.GetReturnValue()->SetDouble(dDividend -
976                                    dDivisor * (int32_t)(dDividend / dDivisor));
977 }
978 
979 // static
Round(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)980 void CFXJSE_FormCalcContext::Round(CFXJSE_Value* pThis,
981                                    const ByteStringView& szFuncName,
982                                    CFXJSE_Arguments& args) {
983   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
984   int32_t argc = args.GetLength();
985   if (argc < 1 || argc > 2) {
986     pContext->ThrowParamCountMismatchException(L"Round");
987     return;
988   }
989 
990   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
991   if (argOne->IsNull()) {
992     args.GetReturnValue()->SetNull();
993     return;
994   }
995 
996   bool dValueRet;
997   double dValue = ExtractDouble(pThis, argOne.get(), &dValueRet);
998   if (!dValueRet) {
999     pContext->ThrowArgumentMismatchException();
1000     return;
1001   }
1002 
1003   uint8_t uPrecision = 0;
1004   if (argc > 1) {
1005     std::unique_ptr<CFXJSE_Value> argTwo = args.GetValue(1);
1006     if (argTwo->IsNull()) {
1007       args.GetReturnValue()->SetNull();
1008       return;
1009     }
1010 
1011     bool dPrecisionRet;
1012     double dPrecision = ExtractDouble(pThis, argTwo.get(), &dPrecisionRet);
1013     if (!dPrecisionRet) {
1014       pContext->ThrowArgumentMismatchException();
1015       return;
1016     }
1017 
1018     uPrecision = static_cast<uint8_t>(pdfium::clamp(dPrecision, 0.0, 12.0));
1019   }
1020 
1021   CFX_Decimal decimalValue(static_cast<float>(dValue), uPrecision);
1022   args.GetReturnValue()->SetDouble(decimalValue);
1023 }
1024 
1025 // static
Sum(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1026 void CFXJSE_FormCalcContext::Sum(CFXJSE_Value* pThis,
1027                                  const ByteStringView& szFuncName,
1028                                  CFXJSE_Arguments& args) {
1029   int32_t argc = args.GetLength();
1030   if (argc == 0) {
1031     args.GetReturnValue()->SetNull();
1032     return;
1033   }
1034 
1035   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
1036   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
1037   uint32_t uCount = 0;
1038   double dSum = 0.0;
1039   for (int32_t i = 0; i < argc; i++) {
1040     std::unique_ptr<CFXJSE_Value> argValue = args.GetValue(i);
1041     if (argValue->IsNull())
1042       continue;
1043 
1044     if (argValue->IsArray()) {
1045       auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
1046       argValue->GetObjectProperty("length", lengthValue.get());
1047       int32_t iLength = lengthValue->ToInteger();
1048       if (iLength <= 2) {
1049         pContext->ThrowArgumentMismatchException();
1050         return;
1051       }
1052 
1053       auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
1054       argValue->GetObjectPropertyByIdx(1, propertyValue.get());
1055       auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
1056       auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
1057       if (propertyValue->IsNull()) {
1058         for (int32_t j = 2; j < iLength; j++) {
1059           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
1060           GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
1061           if (newPropertyValue->IsNull())
1062             continue;
1063 
1064           dSum += ValueToDouble(pThis, jsObjectValue.get());
1065           uCount++;
1066         }
1067       } else {
1068         for (int32_t j = 2; j < iLength; j++) {
1069           argValue->GetObjectPropertyByIdx(j, jsObjectValue.get());
1070           jsObjectValue->GetObjectProperty(
1071               propertyValue->ToString().AsStringView(), newPropertyValue.get());
1072           if (newPropertyValue->IsNull())
1073             continue;
1074 
1075           dSum += ValueToDouble(pThis, newPropertyValue.get());
1076           uCount++;
1077         }
1078       }
1079     } else if (argValue->IsObject()) {
1080       auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
1081       GetObjectDefaultValue(argValue.get(), newPropertyValue.get());
1082       if (newPropertyValue->IsNull())
1083         continue;
1084 
1085       dSum += ValueToDouble(pThis, argValue.get());
1086       uCount++;
1087     } else {
1088       dSum += ValueToDouble(pThis, argValue.get());
1089       uCount++;
1090     }
1091   }
1092   if (uCount == 0) {
1093     args.GetReturnValue()->SetNull();
1094     return;
1095   }
1096 
1097   args.GetReturnValue()->SetDouble(dSum);
1098 }
1099 
1100 // static
Date(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1101 void CFXJSE_FormCalcContext::Date(CFXJSE_Value* pThis,
1102                                   const ByteStringView& szFuncName,
1103                                   CFXJSE_Arguments& args) {
1104   if (args.GetLength() != 0) {
1105     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Date");
1106     return;
1107   }
1108 
1109   time_t currentTime;
1110   time(&currentTime);
1111   struct tm* pTmStruct = gmtime(&currentTime);
1112 
1113   args.GetReturnValue()->SetInteger(DateString2Num(
1114       ByteString::Format("%d%02d%02d", pTmStruct->tm_year + 1900,
1115                          pTmStruct->tm_mon + 1, pTmStruct->tm_mday)
1116           .AsStringView()));
1117 }
1118 
1119 // static
Date2Num(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1120 void CFXJSE_FormCalcContext::Date2Num(CFXJSE_Value* pThis,
1121                                       const ByteStringView& szFuncName,
1122                                       CFXJSE_Arguments& args) {
1123   int32_t argc = args.GetLength();
1124   if (argc < 1 || argc > 3) {
1125     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Date2Num");
1126     return;
1127   }
1128 
1129   std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
1130   if (ValueIsNull(pThis, dateValue.get())) {
1131     args.GetReturnValue()->SetNull();
1132     return;
1133   }
1134 
1135   ByteString dateString = ValueToUTF8String(dateValue.get());
1136   ByteString formatString;
1137   if (argc > 1) {
1138     std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
1139     if (ValueIsNull(pThis, formatValue.get())) {
1140       args.GetReturnValue()->SetNull();
1141       return;
1142     }
1143     formatString = ValueToUTF8String(formatValue.get());
1144   }
1145 
1146   ByteString localString;
1147   if (argc > 2) {
1148     std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
1149     if (ValueIsNull(pThis, localValue.get())) {
1150       args.GetReturnValue()->SetNull();
1151       return;
1152     }
1153     localString = ValueToUTF8String(localValue.get());
1154   }
1155 
1156   ByteString szIsoDateString =
1157       Local2IsoDate(pThis, dateString.AsStringView(),
1158                     formatString.AsStringView(), localString.AsStringView());
1159   args.GetReturnValue()->SetInteger(
1160       DateString2Num(szIsoDateString.AsStringView()));
1161 }
1162 
1163 // static
DateFmt(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1164 void CFXJSE_FormCalcContext::DateFmt(CFXJSE_Value* pThis,
1165                                      const ByteStringView& szFuncName,
1166                                      CFXJSE_Arguments& args) {
1167   int32_t argc = args.GetLength();
1168   if (argc > 2) {
1169     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Date2Num");
1170     return;
1171   }
1172 
1173   int32_t iStyle = 0;
1174   if (argc > 0) {
1175     std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
1176     if (argStyle->IsNull()) {
1177       args.GetReturnValue()->SetNull();
1178       return;
1179     }
1180 
1181     iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
1182     if (iStyle < 0 || iStyle > 4)
1183       iStyle = 0;
1184   }
1185 
1186   ByteString szLocal;
1187   if (argc > 1) {
1188     std::unique_ptr<CFXJSE_Value> argLocal = GetSimpleValue(pThis, args, 1);
1189     if (argLocal->IsNull()) {
1190       args.GetReturnValue()->SetNull();
1191       return;
1192     }
1193     szLocal = ValueToUTF8String(argLocal.get());
1194   }
1195 
1196   ByteString formatStr =
1197       GetStandardDateFormat(pThis, iStyle, szLocal.AsStringView());
1198   args.GetReturnValue()->SetString(formatStr.AsStringView());
1199 }
1200 
1201 // static
IsoDate2Num(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1202 void CFXJSE_FormCalcContext::IsoDate2Num(CFXJSE_Value* pThis,
1203                                          const ByteStringView& szFuncName,
1204                                          CFXJSE_Arguments& args) {
1205   if (args.GetLength() != 1) {
1206     ToJSContext(pThis, nullptr)
1207         ->ThrowParamCountMismatchException(L"IsoDate2Num");
1208     return;
1209   }
1210   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
1211   if (argOne->IsNull()) {
1212     args.GetReturnValue()->SetNull();
1213     return;
1214   }
1215   ByteString szArgString = ValueToUTF8String(argOne.get());
1216   args.GetReturnValue()->SetInteger(DateString2Num(szArgString.AsStringView()));
1217 }
1218 
1219 // static
IsoTime2Num(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1220 void CFXJSE_FormCalcContext::IsoTime2Num(CFXJSE_Value* pThis,
1221                                          const ByteStringView& szFuncName,
1222                                          CFXJSE_Arguments& args) {
1223   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
1224   if (args.GetLength() != 1) {
1225     pContext->ThrowParamCountMismatchException(L"IsoTime2Num");
1226     return;
1227   }
1228 
1229   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
1230   if (ValueIsNull(pThis, argOne.get())) {
1231     args.GetReturnValue()->SetNull();
1232     return;
1233   }
1234 
1235   CXFA_Document* pDoc = pContext->GetDocument();
1236   CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
1237   ByteString szArgString = ValueToUTF8String(argOne.get());
1238   auto pos = szArgString.Find('T', 0);
1239   if (!pos.has_value() || pos.value() == szArgString.GetLength() - 1) {
1240     args.GetReturnValue()->SetInteger(0);
1241     return;
1242   }
1243   szArgString = szArgString.Right(szArgString.GetLength() - (pos.value() + 1));
1244 
1245   CXFA_LocaleValue timeValue(
1246       XFA_VT_TIME, WideString::FromUTF8(szArgString.AsStringView()), pMgr);
1247   if (!timeValue.IsValid()) {
1248     args.GetReturnValue()->SetInteger(0);
1249     return;
1250   }
1251 
1252   CFX_DateTime uniTime = timeValue.GetTime();
1253   int32_t hour = uniTime.GetHour();
1254   int32_t min = uniTime.GetMinute();
1255   int32_t second = uniTime.GetSecond();
1256   int32_t milSecond = uniTime.GetMillisecond();
1257 
1258   // TODO(dsinclair): See if there is other time conversion code in pdfium and
1259   //   consolidate.
1260   int32_t mins = hour * 60 + min;
1261   mins -= (pMgr->GetDefLocale()->GetTimeZone().tzHour * 60);
1262   while (mins > 1440)
1263     mins -= 1440;
1264   while (mins < 0)
1265     mins += 1440;
1266   hour = mins / 60;
1267   min = mins % 60;
1268 
1269   args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
1270                                     second * 1000 + milSecond + 1);
1271 }
1272 
1273 // static
LocalDateFmt(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1274 void CFXJSE_FormCalcContext::LocalDateFmt(CFXJSE_Value* pThis,
1275                                           const ByteStringView& szFuncName,
1276                                           CFXJSE_Arguments& args) {
1277   int32_t argc = args.GetLength();
1278   if (argc > 2) {
1279     ToJSContext(pThis, nullptr)
1280         ->ThrowParamCountMismatchException(L"LocalDateFmt");
1281     return;
1282   }
1283 
1284   int32_t iStyle = 0;
1285   if (argc > 0) {
1286     std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
1287     if (argStyle->IsNull()) {
1288       args.GetReturnValue()->SetNull();
1289       return;
1290     }
1291     iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
1292     if (iStyle > 4 || iStyle < 0)
1293       iStyle = 0;
1294   }
1295 
1296   ByteString szLocal;
1297   if (argc > 1) {
1298     std::unique_ptr<CFXJSE_Value> argLocal = GetSimpleValue(pThis, args, 1);
1299     if (argLocal->IsNull()) {
1300       args.GetReturnValue()->SetNull();
1301       return;
1302     }
1303     szLocal = ValueToUTF8String(argLocal.get());
1304   }
1305 
1306   ByteString formatStr =
1307       GetLocalDateFormat(pThis, iStyle, szLocal.AsStringView(), false);
1308   args.GetReturnValue()->SetString(formatStr.AsStringView());
1309 }
1310 
1311 // static
LocalTimeFmt(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1312 void CFXJSE_FormCalcContext::LocalTimeFmt(CFXJSE_Value* pThis,
1313                                           const ByteStringView& szFuncName,
1314                                           CFXJSE_Arguments& args) {
1315   int32_t argc = args.GetLength();
1316   if (argc > 2) {
1317     ToJSContext(pThis, nullptr)
1318         ->ThrowParamCountMismatchException(L"LocalTimeFmt");
1319     return;
1320   }
1321 
1322   int32_t iStyle = 0;
1323   if (argc > 0) {
1324     std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
1325     if (argStyle->IsNull()) {
1326       args.GetReturnValue()->SetNull();
1327       return;
1328     }
1329     iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
1330     if (iStyle > 4 || iStyle < 0)
1331       iStyle = 0;
1332   }
1333 
1334   ByteString szLocal;
1335   if (argc > 1) {
1336     std::unique_ptr<CFXJSE_Value> argLocal = GetSimpleValue(pThis, args, 1);
1337     if (argLocal->IsNull()) {
1338       args.GetReturnValue()->SetNull();
1339       return;
1340     }
1341     szLocal = ValueToUTF8String(argLocal.get());
1342   }
1343 
1344   ByteString formatStr =
1345       GetLocalTimeFormat(pThis, iStyle, szLocal.AsStringView(), false);
1346   args.GetReturnValue()->SetString(formatStr.AsStringView());
1347 }
1348 
1349 // static
Num2Date(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1350 void CFXJSE_FormCalcContext::Num2Date(CFXJSE_Value* pThis,
1351                                       const ByteStringView& szFuncName,
1352                                       CFXJSE_Arguments& args) {
1353   int32_t argc = args.GetLength();
1354   if (argc < 1 || argc > 3) {
1355     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Num2Date");
1356     return;
1357   }
1358 
1359   std::unique_ptr<CFXJSE_Value> dateValue = GetSimpleValue(pThis, args, 0);
1360   if (ValueIsNull(pThis, dateValue.get())) {
1361     args.GetReturnValue()->SetNull();
1362     return;
1363   }
1364   int32_t dDate = (int32_t)ValueToFloat(pThis, dateValue.get());
1365   if (dDate < 1) {
1366     args.GetReturnValue()->SetNull();
1367     return;
1368   }
1369 
1370   ByteString formatString;
1371   if (argc > 1) {
1372     std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
1373     if (ValueIsNull(pThis, formatValue.get())) {
1374       args.GetReturnValue()->SetNull();
1375       return;
1376     }
1377     formatString = ValueToUTF8String(formatValue.get());
1378   }
1379 
1380   ByteString localString;
1381   if (argc > 2) {
1382     std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
1383     if (ValueIsNull(pThis, localValue.get())) {
1384       args.GetReturnValue()->SetNull();
1385       return;
1386     }
1387     localString = ValueToUTF8String(localValue.get());
1388   }
1389 
1390   int32_t iYear = 1900;
1391   int32_t iMonth = 1;
1392   int32_t iDay = 1;
1393   int32_t i = 0;
1394   while (dDate > 0) {
1395     if (iMonth == 2) {
1396       if ((!((iYear + i) % 4) && ((iYear + i) % 100)) || !((iYear + i) % 400)) {
1397         if (dDate > 29) {
1398           ++iMonth;
1399           if (iMonth > 12) {
1400             iMonth = 1;
1401             ++i;
1402           }
1403           iDay = 1;
1404           dDate -= 29;
1405         } else {
1406           iDay += static_cast<int32_t>(dDate) - 1;
1407           dDate = 0;
1408         }
1409       } else {
1410         if (dDate > 28) {
1411           ++iMonth;
1412           if (iMonth > 12) {
1413             iMonth = 1;
1414             ++i;
1415           }
1416           iDay = 1;
1417           dDate -= 28;
1418         } else {
1419           iDay += static_cast<int32_t>(dDate) - 1;
1420           dDate = 0;
1421         }
1422       }
1423     } else if (iMonth < 8) {
1424       if ((iMonth % 2 == 0)) {
1425         if (dDate > 30) {
1426           ++iMonth;
1427           if (iMonth > 12) {
1428             iMonth = 1;
1429             ++i;
1430           }
1431           iDay = 1;
1432           dDate -= 30;
1433         } else {
1434           iDay += static_cast<int32_t>(dDate) - 1;
1435           dDate = 0;
1436         }
1437       } else {
1438         if (dDate > 31) {
1439           ++iMonth;
1440           if (iMonth > 12) {
1441             iMonth = 1;
1442             ++i;
1443           }
1444           iDay = 1;
1445           dDate -= 31;
1446         } else {
1447           iDay += static_cast<int32_t>(dDate) - 1;
1448           dDate = 0;
1449         }
1450       }
1451     } else {
1452       if (iMonth % 2 != 0) {
1453         if (dDate > 30) {
1454           ++iMonth;
1455           if (iMonth > 12) {
1456             iMonth = 1;
1457             ++i;
1458           }
1459           iDay = 1;
1460           dDate -= 30;
1461         } else {
1462           iDay += static_cast<int32_t>(dDate) - 1;
1463           dDate = 0;
1464         }
1465       } else {
1466         if (dDate > 31) {
1467           ++iMonth;
1468           if (iMonth > 12) {
1469             iMonth = 1;
1470             ++i;
1471           }
1472           iDay = 1;
1473           dDate -= 31;
1474         } else {
1475           iDay += static_cast<int32_t>(dDate) - 1;
1476           dDate = 0;
1477         }
1478       }
1479     }
1480   }
1481 
1482   ByteString szLocalDateString = IsoDate2Local(
1483       pThis,
1484       ByteString::Format("%d%02d%02d", iYear + i, iMonth, iDay).AsStringView(),
1485       formatString.AsStringView(), localString.AsStringView());
1486   args.GetReturnValue()->SetString(szLocalDateString.AsStringView());
1487 }
1488 
1489 // static
Num2GMTime(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1490 void CFXJSE_FormCalcContext::Num2GMTime(CFXJSE_Value* pThis,
1491                                         const ByteStringView& szFuncName,
1492                                         CFXJSE_Arguments& args) {
1493   int32_t argc = args.GetLength();
1494   if (argc < 1 || argc > 3) {
1495     ToJSContext(pThis, nullptr)
1496         ->ThrowParamCountMismatchException(L"Num2GMTime");
1497     return;
1498   }
1499 
1500   std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
1501   if (timeValue->IsNull()) {
1502     args.GetReturnValue()->SetNull();
1503     return;
1504   }
1505   int32_t iTime = (int32_t)ValueToFloat(pThis, timeValue.get());
1506   if (abs(iTime) < 1.0) {
1507     args.GetReturnValue()->SetNull();
1508     return;
1509   }
1510 
1511   ByteString formatString;
1512   if (argc > 1) {
1513     std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
1514     if (formatValue->IsNull()) {
1515       args.GetReturnValue()->SetNull();
1516       return;
1517     }
1518     formatString = ValueToUTF8String(formatValue.get());
1519   }
1520 
1521   ByteString localString;
1522   if (argc > 2) {
1523     std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
1524     if (localValue->IsNull()) {
1525       args.GetReturnValue()->SetNull();
1526       return;
1527     }
1528     localString = ValueToUTF8String(localValue.get());
1529   }
1530 
1531   ByteString szGMTTimeString =
1532       Num2AllTime(pThis, iTime, formatString.AsStringView(),
1533                   localString.AsStringView(), true);
1534   args.GetReturnValue()->SetString(szGMTTimeString.AsStringView());
1535 }
1536 
1537 // static
Num2Time(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1538 void CFXJSE_FormCalcContext::Num2Time(CFXJSE_Value* pThis,
1539                                       const ByteStringView& szFuncName,
1540                                       CFXJSE_Arguments& args) {
1541   int32_t argc = args.GetLength();
1542   if (argc < 1 || argc > 3) {
1543     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Num2Time");
1544     return;
1545   }
1546 
1547   std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
1548   if (timeValue->IsNull()) {
1549     args.GetReturnValue()->SetNull();
1550     return;
1551   }
1552   float fTime = ValueToFloat(pThis, timeValue.get());
1553   if (fabs(fTime) < 1.0) {
1554     args.GetReturnValue()->SetNull();
1555     return;
1556   }
1557 
1558   ByteString formatString;
1559   if (argc > 1) {
1560     std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
1561     if (formatValue->IsNull()) {
1562       args.GetReturnValue()->SetNull();
1563       return;
1564     }
1565     formatString = ValueToUTF8String(formatValue.get());
1566   }
1567 
1568   ByteString localString;
1569   if (argc > 2) {
1570     std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
1571     if (localValue->IsNull()) {
1572       args.GetReturnValue()->SetNull();
1573       return;
1574     }
1575     localString = ValueToUTF8String(localValue.get());
1576   }
1577 
1578   ByteString szLocalTimeString = Num2AllTime(pThis, static_cast<int32_t>(fTime),
1579                                              formatString.AsStringView(),
1580                                              localString.AsStringView(), false);
1581   args.GetReturnValue()->SetString(szLocalTimeString.AsStringView());
1582 }
1583 
1584 // static
Time(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1585 void CFXJSE_FormCalcContext::Time(CFXJSE_Value* pThis,
1586                                   const ByteStringView& szFuncName,
1587                                   CFXJSE_Arguments& args) {
1588   if (args.GetLength() != 0) {
1589     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Time");
1590     return;
1591   }
1592 
1593   time_t now;
1594   time(&now);
1595 
1596   struct tm* pGmt = gmtime(&now);
1597   args.GetReturnValue()->SetInteger(
1598       (pGmt->tm_hour * 3600 + pGmt->tm_min * 60 + pGmt->tm_sec) * 1000);
1599 }
1600 
1601 // static
Time2Num(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1602 void CFXJSE_FormCalcContext::Time2Num(CFXJSE_Value* pThis,
1603                                       const ByteStringView& szFuncName,
1604                                       CFXJSE_Arguments& args) {
1605   int32_t argc = args.GetLength();
1606   if (argc < 1 || argc > 3) {
1607     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Time2Num");
1608     return;
1609   }
1610 
1611   ByteString timeString;
1612   std::unique_ptr<CFXJSE_Value> timeValue = GetSimpleValue(pThis, args, 0);
1613   if (ValueIsNull(pThis, timeValue.get())) {
1614     args.GetReturnValue()->SetNull();
1615     return;
1616   }
1617   timeString = ValueToUTF8String(timeValue.get());
1618 
1619   ByteString formatString;
1620   if (argc > 1) {
1621     std::unique_ptr<CFXJSE_Value> formatValue = GetSimpleValue(pThis, args, 1);
1622     if (ValueIsNull(pThis, formatValue.get())) {
1623       args.GetReturnValue()->SetNull();
1624       return;
1625     }
1626     formatString = ValueToUTF8String(formatValue.get());
1627   }
1628 
1629   ByteString localString;
1630   if (argc > 2) {
1631     std::unique_ptr<CFXJSE_Value> localValue = GetSimpleValue(pThis, args, 2);
1632     if (ValueIsNull(pThis, localValue.get())) {
1633       args.GetReturnValue()->SetNull();
1634       return;
1635     }
1636     localString = ValueToUTF8String(localValue.get());
1637   }
1638 
1639   CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
1640   CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
1641   IFX_Locale* pLocale = nullptr;
1642   if (localString.IsEmpty()) {
1643     CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
1644     ASSERT(pThisNode);
1645     pLocale = pThisNode->GetLocale();
1646   } else {
1647     pLocale =
1648         pMgr->GetLocaleByName(WideString::FromUTF8(localString.AsStringView()));
1649   }
1650 
1651   WideString wsFormat;
1652   if (formatString.IsEmpty())
1653     wsFormat = pLocale->GetTimePattern(FX_LOCALEDATETIMESUBCATEGORY_Default);
1654   else
1655     wsFormat = WideString::FromUTF8(formatString.AsStringView());
1656 
1657   wsFormat = L"time{" + wsFormat + L"}";
1658   CXFA_LocaleValue localeValue(XFA_VT_TIME,
1659                                WideString::FromUTF8(timeString.AsStringView()),
1660                                wsFormat, pLocale, pMgr);
1661   if (!localeValue.IsValid()) {
1662     args.GetReturnValue()->SetInteger(0);
1663     return;
1664   }
1665 
1666   CFX_DateTime uniTime = localeValue.GetTime();
1667   int32_t hour = uniTime.GetHour();
1668   int32_t min = uniTime.GetMinute();
1669   int32_t second = uniTime.GetSecond();
1670   int32_t milSecond = uniTime.GetMillisecond();
1671   int32_t mins = hour * 60 + min;
1672 
1673   mins -= (CXFA_TimeZoneProvider().GetTimeZone().tzHour * 60);
1674   while (mins > 1440)
1675     mins -= 1440;
1676 
1677   while (mins < 0)
1678     mins += 1440;
1679 
1680   hour = mins / 60;
1681   min = mins % 60;
1682   args.GetReturnValue()->SetInteger(hour * 3600000 + min * 60000 +
1683                                     second * 1000 + milSecond + 1);
1684 }
1685 
1686 // static
TimeFmt(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)1687 void CFXJSE_FormCalcContext::TimeFmt(CFXJSE_Value* pThis,
1688                                      const ByteStringView& szFuncName,
1689                                      CFXJSE_Arguments& args) {
1690   int32_t argc = args.GetLength();
1691   if (argc > 2) {
1692     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"TimeFmt");
1693     return;
1694   }
1695 
1696   int32_t iStyle = 0;
1697   if (argc > 0) {
1698     std::unique_ptr<CFXJSE_Value> argStyle = GetSimpleValue(pThis, args, 0);
1699     if (argStyle->IsNull()) {
1700       args.GetReturnValue()->SetNull();
1701       return;
1702     }
1703     iStyle = (int32_t)ValueToFloat(pThis, argStyle.get());
1704     if (iStyle > 4 || iStyle < 0)
1705       iStyle = 0;
1706   }
1707 
1708   ByteString szLocal;
1709   if (argc > 1) {
1710     std::unique_ptr<CFXJSE_Value> argLocal = GetSimpleValue(pThis, args, 1);
1711     if (argLocal->IsNull()) {
1712       args.GetReturnValue()->SetNull();
1713       return;
1714     }
1715     szLocal = ValueToUTF8String(argLocal.get());
1716   }
1717 
1718   ByteString formatStr =
1719       GetStandardTimeFormat(pThis, iStyle, szLocal.AsStringView());
1720   args.GetReturnValue()->SetString(formatStr.AsStringView());
1721 }
1722 
1723 // static
IsIsoDateFormat(const char * pData,int32_t iLength,int32_t & iStyle,int32_t & iYear,int32_t & iMonth,int32_t & iDay)1724 bool CFXJSE_FormCalcContext::IsIsoDateFormat(const char* pData,
1725                                              int32_t iLength,
1726                                              int32_t& iStyle,
1727                                              int32_t& iYear,
1728                                              int32_t& iMonth,
1729                                              int32_t& iDay) {
1730   iYear = 0;
1731   iMonth = 1;
1732   iDay = 1;
1733 
1734   if (iLength < 4)
1735     return false;
1736 
1737   char strYear[5];
1738   strYear[4] = '\0';
1739   for (int32_t i = 0; i < 4; ++i) {
1740     if (!std::isdigit(pData[i]))
1741       return false;
1742 
1743     strYear[i] = pData[i];
1744   }
1745   iYear = FXSYS_atoi(strYear);
1746   iStyle = 0;
1747   if (iLength == 4)
1748     return true;
1749 
1750   iStyle = pData[4] == '-' ? 1 : 0;
1751 
1752   char strTemp[3];
1753   strTemp[2] = '\0';
1754   int32_t iPosOff = iStyle == 0 ? 4 : 5;
1755   if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1]))
1756     return false;
1757 
1758   strTemp[0] = pData[iPosOff];
1759   strTemp[1] = pData[iPosOff + 1];
1760   iMonth = FXSYS_atoi(strTemp);
1761   if (iMonth > 12 || iMonth < 1)
1762     return false;
1763 
1764   if (iStyle == 0) {
1765     iPosOff += 2;
1766     if (iLength == 6)
1767       return true;
1768   } else {
1769     iPosOff += 3;
1770     if (iLength == 7)
1771       return true;
1772   }
1773   if (!std::isdigit(pData[iPosOff]) || !std::isdigit(pData[iPosOff + 1]))
1774     return false;
1775 
1776   strTemp[0] = pData[iPosOff];
1777   strTemp[1] = pData[iPosOff + 1];
1778   iDay = FXSYS_atoi(strTemp);
1779   if (iPosOff + 2 < iLength)
1780     return false;
1781 
1782   if ((!(iYear % 4) && (iYear % 100)) || !(iYear % 400)) {
1783     if (iMonth == 2 && iDay > 29)
1784       return false;
1785   } else {
1786     if (iMonth == 2 && iDay > 28)
1787       return false;
1788   }
1789   if (iMonth != 2) {
1790     if (iMonth < 8) {
1791       if (iDay > (iMonth % 2 == 0 ? 30 : 31))
1792         return false;
1793     } else if (iDay > (iMonth % 2 == 0 ? 31 : 30)) {
1794       return false;
1795     }
1796   }
1797   return true;
1798 }
1799 
1800 // static
IsIsoTimeFormat(const char * pData,int32_t iLength,int32_t & iHour,int32_t & iMinute,int32_t & iSecond,int32_t & iMilliSecond,int32_t & iZoneHour,int32_t & iZoneMinute)1801 bool CFXJSE_FormCalcContext::IsIsoTimeFormat(const char* pData,
1802                                              int32_t iLength,
1803                                              int32_t& iHour,
1804                                              int32_t& iMinute,
1805                                              int32_t& iSecond,
1806                                              int32_t& iMilliSecond,
1807                                              int32_t& iZoneHour,
1808                                              int32_t& iZoneMinute) {
1809   iHour = 0;
1810   iMinute = 0;
1811   iSecond = 0;
1812   iMilliSecond = 0;
1813   iZoneHour = 0;
1814   iZoneMinute = 0;
1815   if (!pData)
1816     return false;
1817 
1818   char strTemp[3];
1819   strTemp[2] = '\0';
1820   int32_t iZone = 0;
1821   int32_t i = 0;
1822   while (i < iLength) {
1823     if (!std::isdigit(pData[i]) && pData[i] != ':') {
1824       iZone = i;
1825       break;
1826     }
1827     ++i;
1828   }
1829   if (i == iLength)
1830     iZone = iLength;
1831 
1832   int32_t iPos = 0;
1833   int32_t iIndex = 0;
1834   while (iIndex < iZone) {
1835     if (!std::isdigit(pData[iIndex]))
1836       return false;
1837 
1838     strTemp[0] = pData[iIndex];
1839     if (!std::isdigit(pData[iIndex + 1]))
1840       return false;
1841 
1842     strTemp[1] = pData[iIndex + 1];
1843     if (FXSYS_atoi(strTemp) > 60)
1844       return false;
1845 
1846     if (pData[2] == ':') {
1847       if (iPos == 0) {
1848         iHour = FXSYS_atoi(strTemp);
1849         ++iPos;
1850       } else if (iPos == 1) {
1851         iMinute = FXSYS_atoi(strTemp);
1852         ++iPos;
1853       } else {
1854         iSecond = FXSYS_atoi(strTemp);
1855       }
1856       iIndex += 3;
1857     } else {
1858       if (iPos == 0) {
1859         iHour = FXSYS_atoi(strTemp);
1860         ++iPos;
1861       } else if (iPos == 1) {
1862         iMinute = FXSYS_atoi(strTemp);
1863         ++iPos;
1864       } else if (iPos == 2) {
1865         iSecond = FXSYS_atoi(strTemp);
1866         ++iPos;
1867       }
1868       iIndex += 2;
1869     }
1870   }
1871 
1872   if (iIndex < iLength && pData[iIndex] == '.') {
1873     constexpr int kSubSecondLength = 3;
1874     if (iIndex + kSubSecondLength >= iLength)
1875       return false;
1876 
1877     ++iIndex;
1878     char strSec[kSubSecondLength + 1];
1879     for (int i = 0; i < kSubSecondLength; ++i) {
1880       char c = pData[iIndex + i];
1881       if (!std::isdigit(c))
1882         return false;
1883       strSec[i] = c;
1884     }
1885     strSec[kSubSecondLength] = '\0';
1886 
1887     iMilliSecond = FXSYS_atoi(strSec);
1888     if (iMilliSecond > 100) {
1889       iMilliSecond = 0;
1890       return false;
1891     }
1892     iIndex += kSubSecondLength;
1893   }
1894 
1895   if (iIndex < iLength && FXSYS_tolower(pData[iIndex]) == 'z')
1896     return true;
1897 
1898   int32_t iSign = 1;
1899   if (iIndex < iLength) {
1900     if (pData[iIndex] == '+') {
1901       ++iIndex;
1902     } else if (pData[iIndex] == '-') {
1903       iSign = -1;
1904       ++iIndex;
1905     }
1906   }
1907   iPos = 0;
1908   while (iIndex < iLength) {
1909     if (!std::isdigit(pData[iIndex]))
1910       return false;
1911 
1912     strTemp[0] = pData[iIndex];
1913     if (!std::isdigit(pData[iIndex + 1]))
1914       return false;
1915 
1916     strTemp[1] = pData[iIndex + 1];
1917     if (FXSYS_atoi(strTemp) > 60)
1918       return false;
1919 
1920     if (pData[2] == ':') {
1921       if (iPos == 0) {
1922         iZoneHour = FXSYS_atoi(strTemp);
1923       } else if (iPos == 1) {
1924         iZoneMinute = FXSYS_atoi(strTemp);
1925       }
1926       iIndex += 3;
1927     } else {
1928       if (!iPos) {
1929         iZoneHour = FXSYS_atoi(strTemp);
1930         ++iPos;
1931       } else if (iPos == 1) {
1932         iZoneMinute = FXSYS_atoi(strTemp);
1933         ++iPos;
1934       }
1935       iIndex += 2;
1936     }
1937   }
1938   if (iIndex < iLength)
1939     return false;
1940 
1941   iZoneHour *= iSign;
1942   return true;
1943 }
1944 
1945 // static
IsIsoDateTimeFormat(const char * pData,int32_t iLength,int32_t & iYear,int32_t & iMonth,int32_t & iDay,int32_t & iHour,int32_t & iMinute,int32_t & iSecond,int32_t & iMillionSecond,int32_t & iZoneHour,int32_t & iZoneMinute)1946 bool CFXJSE_FormCalcContext::IsIsoDateTimeFormat(const char* pData,
1947                                                  int32_t iLength,
1948                                                  int32_t& iYear,
1949                                                  int32_t& iMonth,
1950                                                  int32_t& iDay,
1951                                                  int32_t& iHour,
1952                                                  int32_t& iMinute,
1953                                                  int32_t& iSecond,
1954                                                  int32_t& iMillionSecond,
1955                                                  int32_t& iZoneHour,
1956                                                  int32_t& iZoneMinute) {
1957   iYear = 0;
1958   iMonth = 0;
1959   iDay = 0;
1960   iHour = 0;
1961   iMinute = 0;
1962   iSecond = 0;
1963   if (!pData)
1964     return false;
1965 
1966   int32_t iIndex = 0;
1967   while (pData[iIndex] != 'T' && pData[iIndex] != 't') {
1968     if (iIndex >= iLength)
1969       return false;
1970     ++iIndex;
1971   }
1972   if (iIndex != 8 && iIndex != 10)
1973     return false;
1974 
1975   int32_t iStyle = -1;
1976   if (!IsIsoDateFormat(pData, iIndex, iStyle, iYear, iMonth, iDay))
1977     return false;
1978   if (pData[iIndex] != 'T' && pData[iIndex] != 't')
1979     return true;
1980 
1981   ++iIndex;
1982   if (((iLength - iIndex > 13) && (iLength - iIndex < 6)) &&
1983       (iLength - iIndex != 15)) {
1984     return true;
1985   }
1986   return IsIsoTimeFormat(pData + iIndex, iLength - iIndex, iHour, iMinute,
1987                          iSecond, iMillionSecond, iZoneHour, iZoneMinute);
1988 }
1989 
1990 // static
Local2IsoDate(CFXJSE_Value * pThis,const ByteStringView & szDate,const ByteStringView & szFormat,const ByteStringView & szLocale)1991 ByteString CFXJSE_FormCalcContext::Local2IsoDate(
1992     CFXJSE_Value* pThis,
1993     const ByteStringView& szDate,
1994     const ByteStringView& szFormat,
1995     const ByteStringView& szLocale) {
1996   CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
1997   if (!pDoc)
1998     return ByteString();
1999 
2000   CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
2001   IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
2002   if (!pLocale)
2003     return ByteString();
2004 
2005   WideString wsFormat = FormatFromString(pLocale, szFormat);
2006   CFX_DateTime dt = CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(szDate),
2007                                      wsFormat, pLocale, pMgr)
2008                         .GetDate();
2009 
2010   return ByteString::Format("%4d-%02d-%02d", dt.GetYear(), dt.GetMonth(),
2011                             dt.GetDay());
2012 }
2013 
2014 // static
IsoDate2Local(CFXJSE_Value * pThis,const ByteStringView & szDate,const ByteStringView & szFormat,const ByteStringView & szLocale)2015 ByteString CFXJSE_FormCalcContext::IsoDate2Local(
2016     CFXJSE_Value* pThis,
2017     const ByteStringView& szDate,
2018     const ByteStringView& szFormat,
2019     const ByteStringView& szLocale) {
2020   CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
2021   if (!pDoc)
2022     return ByteString();
2023 
2024   CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
2025   IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
2026   if (!pLocale)
2027     return ByteString();
2028 
2029   WideString wsFormat = FormatFromString(pLocale, szFormat);
2030   WideString wsRet;
2031   CXFA_LocaleValue(XFA_VT_DATE, WideString::FromUTF8(szDate), pMgr)
2032       .FormatPatterns(wsRet, wsFormat, pLocale, XFA_VALUEPICTURE_Display);
2033   return wsRet.UTF8Encode();
2034 }
2035 
2036 // static
IsoTime2Local(CFXJSE_Value * pThis,const ByteStringView & szTime,const ByteStringView & szFormat,const ByteStringView & szLocale)2037 ByteString CFXJSE_FormCalcContext::IsoTime2Local(
2038     CFXJSE_Value* pThis,
2039     const ByteStringView& szTime,
2040     const ByteStringView& szFormat,
2041     const ByteStringView& szLocale) {
2042   CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
2043   if (!pDoc)
2044     return ByteString();
2045 
2046   CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
2047   IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
2048   if (!pLocale)
2049     return ByteString();
2050 
2051   WideString wsFormat = {
2052       L"time{", FormatFromString(pLocale, szFormat).AsStringView(), L"}"};
2053   CXFA_LocaleValue widgetValue(XFA_VT_TIME, WideString::FromUTF8(szTime), pMgr);
2054   WideString wsRet;
2055   widgetValue.FormatPatterns(wsRet, wsFormat, pLocale,
2056                              XFA_VALUEPICTURE_Display);
2057   return wsRet.UTF8Encode();
2058 }
2059 
2060 // static
DateString2Num(const ByteStringView & szDateString)2061 int32_t CFXJSE_FormCalcContext::DateString2Num(
2062     const ByteStringView& szDateString) {
2063   int32_t iLength = szDateString.GetLength();
2064   int32_t iYear = 0;
2065   int32_t iMonth = 0;
2066   int32_t iDay = 0;
2067   if (iLength <= 10) {
2068     int32_t iStyle = -1;
2069     if (!IsIsoDateFormat(szDateString.unterminated_c_str(), iLength, iStyle,
2070                          iYear, iMonth, iDay)) {
2071       return 0;
2072     }
2073   } else {
2074     int32_t iHour = 0;
2075     int32_t iMinute = 0;
2076     int32_t iSecond = 0;
2077     int32_t iMilliSecond = 0;
2078     int32_t iZoneHour = 0;
2079     int32_t iZoneMinute = 0;
2080     if (!IsIsoDateTimeFormat(szDateString.unterminated_c_str(), iLength, iYear,
2081                              iMonth, iDay, iHour, iMinute, iSecond,
2082                              iMilliSecond, iZoneHour, iZoneMinute)) {
2083       return 0;
2084     }
2085   }
2086 
2087   float dDays = 0;
2088   int32_t i = 1;
2089   if (iYear < 1900)
2090     return 0;
2091 
2092   while (iYear - i >= 1900) {
2093     dDays +=
2094         ((!((iYear - i) % 4) && ((iYear - i) % 100)) || !((iYear - i) % 400))
2095             ? 366
2096             : 365;
2097     ++i;
2098   }
2099   i = 1;
2100   while (i < iMonth) {
2101     if (i == 2)
2102       dDays += ((!(iYear % 4) && (iYear % 100)) || !(iYear % 400)) ? 29 : 28;
2103     else if (i <= 7)
2104       dDays += (i % 2 == 0) ? 30 : 31;
2105     else
2106       dDays += (i % 2 == 0) ? 31 : 30;
2107 
2108     ++i;
2109   }
2110   i = 0;
2111   while (iDay - i > 0) {
2112     dDays += 1;
2113     ++i;
2114   }
2115   return (int32_t)dDays;
2116 }
2117 
2118 // static
GetLocalDateFormat(CFXJSE_Value * pThis,int32_t iStyle,const ByteStringView & szLocale,bool bStandard)2119 ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(
2120     CFXJSE_Value* pThis,
2121     int32_t iStyle,
2122     const ByteStringView& szLocale,
2123     bool bStandard) {
2124   CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
2125   if (!pDoc)
2126     return ByteString();
2127 
2128   CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
2129   IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
2130   if (!pLocale)
2131     return ByteString();
2132 
2133   WideString strRet = pLocale->GetDatePattern(SubCategoryFromInt(iStyle));
2134   if (!bStandard) {
2135     AlternateDateTimeSymbols(strRet, pLocale->GetDateTimeSymbols(),
2136                              g_sAltTable_Date);
2137   }
2138   return strRet.UTF8Encode();
2139 }
2140 
2141 // static
GetLocalTimeFormat(CFXJSE_Value * pThis,int32_t iStyle,const ByteStringView & szLocale,bool bStandard)2142 ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(
2143     CFXJSE_Value* pThis,
2144     int32_t iStyle,
2145     const ByteStringView& szLocale,
2146     bool bStandard) {
2147   CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
2148   if (!pDoc)
2149     return ByteString();
2150 
2151   CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
2152   IFX_Locale* pLocale = LocaleFromString(pDoc, pMgr, szLocale);
2153   if (!pLocale)
2154     return ByteString();
2155 
2156   WideString strRet = pLocale->GetTimePattern(SubCategoryFromInt(iStyle));
2157   if (!bStandard) {
2158     AlternateDateTimeSymbols(strRet, pLocale->GetDateTimeSymbols(),
2159                              g_sAltTable_Time);
2160   }
2161   return strRet.UTF8Encode();
2162 }
2163 
2164 // static
GetStandardDateFormat(CFXJSE_Value * pThis,int32_t iStyle,const ByteStringView & szLocalStr)2165 ByteString CFXJSE_FormCalcContext::GetStandardDateFormat(
2166     CFXJSE_Value* pThis,
2167     int32_t iStyle,
2168     const ByteStringView& szLocalStr) {
2169   return GetLocalDateFormat(pThis, iStyle, szLocalStr, true);
2170 }
2171 
2172 // static
GetStandardTimeFormat(CFXJSE_Value * pThis,int32_t iStyle,const ByteStringView & szLocalStr)2173 ByteString CFXJSE_FormCalcContext::GetStandardTimeFormat(
2174     CFXJSE_Value* pThis,
2175     int32_t iStyle,
2176     const ByteStringView& szLocalStr) {
2177   return GetLocalTimeFormat(pThis, iStyle, szLocalStr, true);
2178 }
2179 
2180 // static
Num2AllTime(CFXJSE_Value * pThis,int32_t iTime,const ByteStringView & szFormat,const ByteStringView & szLocale,bool bGM)2181 ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_Value* pThis,
2182                                                int32_t iTime,
2183                                                const ByteStringView& szFormat,
2184                                                const ByteStringView& szLocale,
2185                                                bool bGM) {
2186   int32_t iHour = 0;
2187   int32_t iMin = 0;
2188   int32_t iSec = 0;
2189   iHour = static_cast<int>(iTime) / 3600000;
2190   iMin = (static_cast<int>(iTime) - iHour * 3600000) / 60000;
2191   iSec = (static_cast<int>(iTime) - iHour * 3600000 - iMin * 60000) / 1000;
2192 
2193   if (!bGM) {
2194     int32_t iZoneHour = 0;
2195     int32_t iZoneMin = 0;
2196     int32_t iZoneSec = 0;
2197     GetLocalTimeZone(iZoneHour, iZoneMin, iZoneSec);
2198     iHour += iZoneHour;
2199     iMin += iZoneMin;
2200     iSec += iZoneSec;
2201   }
2202 
2203   return IsoTime2Local(
2204       pThis,
2205       ByteString::Format("%02d:%02d:%02d", iHour, iMin, iSec).AsStringView(),
2206       szFormat, szLocale);
2207 }
2208 
2209 // static
GetLocalTimeZone(int32_t & iHour,int32_t & iMin,int32_t & iSec)2210 void CFXJSE_FormCalcContext::GetLocalTimeZone(int32_t& iHour,
2211                                               int32_t& iMin,
2212                                               int32_t& iSec) {
2213   time_t now;
2214   time(&now);
2215 
2216   struct tm* pGmt = gmtime(&now);
2217   struct tm* pLocal = localtime(&now);
2218   iHour = pLocal->tm_hour - pGmt->tm_hour;
2219   iMin = pLocal->tm_min - pGmt->tm_min;
2220   iSec = pLocal->tm_sec - pGmt->tm_sec;
2221 }
2222 
2223 // static
Apr(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2224 void CFXJSE_FormCalcContext::Apr(CFXJSE_Value* pThis,
2225                                  const ByteStringView& szFuncName,
2226                                  CFXJSE_Arguments& args) {
2227   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2228   if (args.GetLength() != 3) {
2229     pContext->ThrowParamCountMismatchException(L"Apr");
2230     return;
2231   }
2232 
2233   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2234   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
2235   std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
2236   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
2237       ValueIsNull(pThis, argThree.get())) {
2238     args.GetReturnValue()->SetNull();
2239     return;
2240   }
2241 
2242   double nPrincipal = ValueToDouble(pThis, argOne.get());
2243   double nPayment = ValueToDouble(pThis, argTwo.get());
2244   double nPeriods = ValueToDouble(pThis, argThree.get());
2245   if (nPrincipal <= 0 || nPayment <= 0 || nPeriods <= 0) {
2246     pContext->ThrowArgumentMismatchException();
2247     return;
2248   }
2249 
2250   double r = 2 * (nPeriods * nPayment - nPrincipal) / (nPeriods * nPrincipal);
2251   double nTemp = 1;
2252   for (int32_t i = 0; i < nPeriods; ++i)
2253     nTemp *= (1 + r);
2254 
2255   double nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
2256   while (fabs(nRet) > kFinancialPrecision) {
2257     double nDerivative =
2258         ((nTemp + r * nPeriods * (nTemp / (1 + r))) * (nTemp - 1) -
2259          (r * nTemp * nPeriods * (nTemp / (1 + r)))) /
2260         ((nTemp - 1) * (nTemp - 1));
2261     if (nDerivative == 0) {
2262       args.GetReturnValue()->SetNull();
2263       return;
2264     }
2265 
2266     r = r - nRet / nDerivative;
2267     nTemp = 1;
2268     for (int32_t i = 0; i < nPeriods; ++i) {
2269       nTemp *= (1 + r);
2270     }
2271     nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal;
2272   }
2273   args.GetReturnValue()->SetDouble(r * 12);
2274 }
2275 
2276 // static
CTerm(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2277 void CFXJSE_FormCalcContext::CTerm(CFXJSE_Value* pThis,
2278                                    const ByteStringView& szFuncName,
2279                                    CFXJSE_Arguments& args) {
2280   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2281   if (args.GetLength() != 3) {
2282     pContext->ThrowParamCountMismatchException(L"CTerm");
2283     return;
2284   }
2285 
2286   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2287   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
2288   std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
2289   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
2290       ValueIsNull(pThis, argThree.get())) {
2291     args.GetReturnValue()->SetNull();
2292     return;
2293   }
2294 
2295   float nRate = ValueToFloat(pThis, argOne.get());
2296   float nFutureValue = ValueToFloat(pThis, argTwo.get());
2297   float nInitAmount = ValueToFloat(pThis, argThree.get());
2298   if ((nRate <= 0) || (nFutureValue <= 0) || (nInitAmount <= 0)) {
2299     pContext->ThrowArgumentMismatchException();
2300     return;
2301   }
2302 
2303   args.GetReturnValue()->SetFloat(log((float)(nFutureValue / nInitAmount)) /
2304                                   log((float)(1 + nRate)));
2305 }
2306 
2307 // static
FV(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2308 void CFXJSE_FormCalcContext::FV(CFXJSE_Value* pThis,
2309                                 const ByteStringView& szFuncName,
2310                                 CFXJSE_Arguments& args) {
2311   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2312   if (args.GetLength() != 3) {
2313     pContext->ThrowParamCountMismatchException(L"FV");
2314     return;
2315   }
2316 
2317   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2318   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
2319   std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
2320   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
2321       ValueIsNull(pThis, argThree.get())) {
2322     args.GetReturnValue()->SetNull();
2323     return;
2324   }
2325 
2326   double nAmount = ValueToDouble(pThis, argOne.get());
2327   double nRate = ValueToDouble(pThis, argTwo.get());
2328   double nPeriod = ValueToDouble(pThis, argThree.get());
2329   if ((nRate < 0) || (nPeriod <= 0) || (nAmount <= 0)) {
2330     pContext->ThrowArgumentMismatchException();
2331     return;
2332   }
2333 
2334   double dResult = 0;
2335   if (nRate) {
2336     double nTemp = 1;
2337     for (int i = 0; i < nPeriod; ++i) {
2338       nTemp *= 1 + nRate;
2339     }
2340     dResult = nAmount * (nTemp - 1) / nRate;
2341   } else {
2342     dResult = nAmount * nPeriod;
2343   }
2344 
2345   args.GetReturnValue()->SetDouble(dResult);
2346 }
2347 
2348 // static
IPmt(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2349 void CFXJSE_FormCalcContext::IPmt(CFXJSE_Value* pThis,
2350                                   const ByteStringView& szFuncName,
2351                                   CFXJSE_Arguments& args) {
2352   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2353   if (args.GetLength() != 5) {
2354     pContext->ThrowParamCountMismatchException(L"IPmt");
2355     return;
2356   }
2357 
2358   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2359   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
2360   std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
2361   std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
2362   std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
2363   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
2364       ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
2365       ValueIsNull(pThis, argFive.get())) {
2366     args.GetReturnValue()->SetNull();
2367     return;
2368   }
2369 
2370   float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
2371   float nRate = ValueToFloat(pThis, argTwo.get());
2372   float nPayment = ValueToFloat(pThis, argThree.get());
2373   float nFirstMonth = ValueToFloat(pThis, argFour.get());
2374   float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
2375   if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
2376       (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
2377     pContext->ThrowArgumentMismatchException();
2378     return;
2379   }
2380 
2381   float nRateOfMonth = nRate / 12;
2382   int32_t iNums =
2383       (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
2384                  log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
2385                 log10((float)(1 + nRateOfMonth)));
2386   int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
2387 
2388   if (nPayment < nPrincipalAmount * nRateOfMonth) {
2389     args.GetReturnValue()->SetFloat(0);
2390     return;
2391   }
2392 
2393   int32_t i = 0;
2394   for (i = 0; i < nFirstMonth - 1; ++i)
2395     nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
2396 
2397   float nSum = 0;
2398   for (; i < iEnd; ++i) {
2399     nSum += nPrincipalAmount * nRateOfMonth;
2400     nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
2401   }
2402   args.GetReturnValue()->SetFloat(nSum);
2403 }
2404 
2405 // static
NPV(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2406 void CFXJSE_FormCalcContext::NPV(CFXJSE_Value* pThis,
2407                                  const ByteStringView& szFuncName,
2408                                  CFXJSE_Arguments& args) {
2409   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2410   int32_t argc = args.GetLength();
2411   if (argc < 3) {
2412     pContext->ThrowParamCountMismatchException(L"NPV");
2413     return;
2414   }
2415 
2416   std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
2417   for (int32_t i = 0; i < argc; i++) {
2418     argValues.push_back(GetSimpleValue(pThis, args, i));
2419     if (ValueIsNull(pThis, argValues[i].get())) {
2420       args.GetReturnValue()->SetNull();
2421       return;
2422     }
2423   }
2424 
2425   double nRate = ValueToDouble(pThis, argValues[0].get());
2426   if (nRate <= 0) {
2427     pContext->ThrowArgumentMismatchException();
2428     return;
2429   }
2430 
2431   std::vector<double> data(argc - 1);
2432   for (int32_t i = 1; i < argc; i++)
2433     data.push_back(ValueToDouble(pThis, argValues[i].get()));
2434 
2435   double nSum = 0;
2436   int32_t iIndex = 0;
2437   for (int32_t i = 0; i < argc - 1; i++) {
2438     double nTemp = 1;
2439     for (int32_t j = 0; j <= i; j++)
2440       nTemp *= 1 + nRate;
2441 
2442     double nNum = data[iIndex++];
2443     nSum += nNum / nTemp;
2444   }
2445   args.GetReturnValue()->SetDouble(nSum);
2446 }
2447 
2448 // static
Pmt(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2449 void CFXJSE_FormCalcContext::Pmt(CFXJSE_Value* pThis,
2450                                  const ByteStringView& szFuncName,
2451                                  CFXJSE_Arguments& args) {
2452   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2453   if (args.GetLength() != 3) {
2454     pContext->ThrowParamCountMismatchException(L"Pmt");
2455     return;
2456   }
2457 
2458   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2459   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
2460   std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
2461   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
2462       ValueIsNull(pThis, argThree.get())) {
2463     args.GetReturnValue()->SetNull();
2464     return;
2465   }
2466 
2467   float nPrincipal = ValueToFloat(pThis, argOne.get());
2468   float nRate = ValueToFloat(pThis, argTwo.get());
2469   float nPeriods = ValueToFloat(pThis, argThree.get());
2470   if ((nPrincipal <= 0) || (nRate <= 0) || (nPeriods <= 0)) {
2471     pContext->ThrowArgumentMismatchException();
2472     return;
2473   }
2474 
2475   float nTmp = 1 + nRate;
2476   float nSum = nTmp;
2477   for (int32_t i = 0; i < nPeriods - 1; ++i)
2478     nSum *= nTmp;
2479 
2480   args.GetReturnValue()->SetFloat((nPrincipal * nRate * nSum) / (nSum - 1));
2481 }
2482 
2483 // static
PPmt(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2484 void CFXJSE_FormCalcContext::PPmt(CFXJSE_Value* pThis,
2485                                   const ByteStringView& szFuncName,
2486                                   CFXJSE_Arguments& args) {
2487   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2488   if (args.GetLength() != 5) {
2489     pContext->ThrowParamCountMismatchException(L"PPmt");
2490     return;
2491   }
2492 
2493   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2494   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
2495   std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
2496   std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
2497   std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
2498   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
2499       ValueIsNull(pThis, argThree.get()) || ValueIsNull(pThis, argFour.get()) ||
2500       ValueIsNull(pThis, argFive.get())) {
2501     args.GetReturnValue()->SetNull();
2502     return;
2503   }
2504 
2505   float nPrincipalAmount = ValueToFloat(pThis, argOne.get());
2506   float nRate = ValueToFloat(pThis, argTwo.get());
2507   float nPayment = ValueToFloat(pThis, argThree.get());
2508   float nFirstMonth = ValueToFloat(pThis, argFour.get());
2509   float nNumberOfMonths = ValueToFloat(pThis, argFive.get());
2510   if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) ||
2511       (nFirstMonth < 0) || (nNumberOfMonths < 0)) {
2512     pContext->ThrowArgumentMismatchException();
2513     return;
2514   }
2515 
2516   float nRateOfMonth = nRate / 12;
2517   int32_t iNums =
2518       (int32_t)((log10((float)(nPayment / nPrincipalAmount)) -
2519                  log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) /
2520                 log10((float)(1 + nRateOfMonth)));
2521   int32_t iEnd = std::min((int32_t)(nFirstMonth + nNumberOfMonths - 1), iNums);
2522   if (nPayment < nPrincipalAmount * nRateOfMonth) {
2523     pContext->ThrowArgumentMismatchException();
2524     return;
2525   }
2526 
2527   int32_t i = 0;
2528   for (i = 0; i < nFirstMonth - 1; ++i)
2529     nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth;
2530 
2531   float nTemp = 0;
2532   float nSum = 0;
2533   for (; i < iEnd; ++i) {
2534     nTemp = nPayment - nPrincipalAmount * nRateOfMonth;
2535     nSum += nTemp;
2536     nPrincipalAmount -= nTemp;
2537   }
2538   args.GetReturnValue()->SetFloat(nSum);
2539 }
2540 
2541 // static
PV(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2542 void CFXJSE_FormCalcContext::PV(CFXJSE_Value* pThis,
2543                                 const ByteStringView& szFuncName,
2544                                 CFXJSE_Arguments& args) {
2545   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2546   if (args.GetLength() != 3) {
2547     pContext->ThrowParamCountMismatchException(L"PV");
2548     return;
2549   }
2550 
2551   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2552   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
2553   std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
2554   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
2555       ValueIsNull(pThis, argThree.get())) {
2556     args.GetReturnValue()->SetNull();
2557     return;
2558   }
2559 
2560   double nAmount = ValueToDouble(pThis, argOne.get());
2561   double nRate = ValueToDouble(pThis, argTwo.get());
2562   double nPeriod = ValueToDouble(pThis, argThree.get());
2563   if ((nAmount <= 0) || (nRate < 0) || (nPeriod <= 0)) {
2564     pContext->ThrowArgumentMismatchException();
2565     return;
2566   }
2567 
2568   double nTemp = 1;
2569   for (int32_t i = 0; i < nPeriod; ++i)
2570     nTemp *= 1 + nRate;
2571 
2572   nTemp = 1 / nTemp;
2573   args.GetReturnValue()->SetDouble(nAmount * ((1 - nTemp) / nRate));
2574 }
2575 
2576 // static
Rate(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2577 void CFXJSE_FormCalcContext::Rate(CFXJSE_Value* pThis,
2578                                   const ByteStringView& szFuncName,
2579                                   CFXJSE_Arguments& args) {
2580   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2581   if (args.GetLength() != 3) {
2582     pContext->ThrowParamCountMismatchException(L"Rate");
2583     return;
2584   }
2585 
2586   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2587   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
2588   std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
2589   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
2590       ValueIsNull(pThis, argThree.get())) {
2591     args.GetReturnValue()->SetNull();
2592     return;
2593   }
2594 
2595   float nFuture = ValueToFloat(pThis, argOne.get());
2596   float nPresent = ValueToFloat(pThis, argTwo.get());
2597   float nTotalNumber = ValueToFloat(pThis, argThree.get());
2598   if ((nFuture <= 0) || (nPresent < 0) || (nTotalNumber <= 0)) {
2599     pContext->ThrowArgumentMismatchException();
2600     return;
2601   }
2602 
2603   args.GetReturnValue()->SetFloat(
2604       FXSYS_pow((float)(nFuture / nPresent), (float)(1 / nTotalNumber)) - 1);
2605 }
2606 
2607 // static
Term(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2608 void CFXJSE_FormCalcContext::Term(CFXJSE_Value* pThis,
2609                                   const ByteStringView& szFuncName,
2610                                   CFXJSE_Arguments& args) {
2611   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2612   if (args.GetLength() != 3) {
2613     pContext->ThrowParamCountMismatchException(L"Term");
2614     return;
2615   }
2616 
2617   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2618   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
2619   std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
2620   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get()) ||
2621       ValueIsNull(pThis, argThree.get())) {
2622     args.GetReturnValue()->SetNull();
2623     return;
2624   }
2625 
2626   float nMount = ValueToFloat(pThis, argOne.get());
2627   float nRate = ValueToFloat(pThis, argTwo.get());
2628   float nFuture = ValueToFloat(pThis, argThree.get());
2629   if ((nMount <= 0) || (nRate <= 0) || (nFuture <= 0)) {
2630     pContext->ThrowArgumentMismatchException();
2631     return;
2632   }
2633 
2634   args.GetReturnValue()->SetFloat(log((float)(nFuture / nMount * nRate) + 1) /
2635                                   log((float)(1 + nRate)));
2636 }
2637 
2638 // static
Choose(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2639 void CFXJSE_FormCalcContext::Choose(CFXJSE_Value* pThis,
2640                                     const ByteStringView& szFuncName,
2641                                     CFXJSE_Arguments& args) {
2642   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2643   int32_t argc = args.GetLength();
2644   if (argc < 2) {
2645     pContext->ThrowParamCountMismatchException(L"Choose");
2646     return;
2647   }
2648 
2649   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
2650   if (ValueIsNull(pThis, argOne.get())) {
2651     args.GetReturnValue()->SetNull();
2652     return;
2653   }
2654 
2655   int32_t iIndex = (int32_t)ValueToFloat(pThis, argOne.get());
2656   if (iIndex < 1) {
2657     args.GetReturnValue()->SetString("");
2658     return;
2659   }
2660 
2661   bool bFound = false;
2662   bool bStopCounterFlags = false;
2663   int32_t iArgIndex = 1;
2664   int32_t iValueIndex = 0;
2665   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
2666   while (!bFound && !bStopCounterFlags && (iArgIndex < argc)) {
2667     std::unique_ptr<CFXJSE_Value> argIndexValue = args.GetValue(iArgIndex);
2668     if (argIndexValue->IsArray()) {
2669       auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
2670       argIndexValue->GetObjectProperty("length", lengthValue.get());
2671       int32_t iLength = lengthValue->ToInteger();
2672       if (iLength > 3)
2673         bStopCounterFlags = true;
2674 
2675       iValueIndex += (iLength - 2);
2676       if (iValueIndex >= iIndex) {
2677         auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
2678         auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
2679         auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
2680         argIndexValue->GetObjectPropertyByIdx(1, propertyValue.get());
2681         argIndexValue->GetObjectPropertyByIdx(
2682             (iLength - 1) - (iValueIndex - iIndex), jsObjectValue.get());
2683         if (propertyValue->IsNull()) {
2684           GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
2685         } else {
2686           jsObjectValue->GetObjectProperty(
2687               propertyValue->ToString().AsStringView(), newPropertyValue.get());
2688         }
2689         ByteString bsChosen = ValueToUTF8String(newPropertyValue.get());
2690         args.GetReturnValue()->SetString(bsChosen.AsStringView());
2691         bFound = true;
2692       }
2693     } else {
2694       iValueIndex++;
2695       if (iValueIndex == iIndex) {
2696         ByteString bsChosen = ValueToUTF8String(argIndexValue.get());
2697         args.GetReturnValue()->SetString(bsChosen.AsStringView());
2698         bFound = true;
2699       }
2700     }
2701     iArgIndex++;
2702   }
2703   if (!bFound)
2704     args.GetReturnValue()->SetString("");
2705 }
2706 
2707 // static
Exists(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2708 void CFXJSE_FormCalcContext::Exists(CFXJSE_Value* pThis,
2709                                     const ByteStringView& szFuncName,
2710                                     CFXJSE_Arguments& args) {
2711   if (args.GetLength() != 1) {
2712     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Exists");
2713     return;
2714   }
2715   args.GetReturnValue()->SetInteger(args.GetValue(0)->IsObject());
2716 }
2717 
2718 // static
HasValue(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2719 void CFXJSE_FormCalcContext::HasValue(CFXJSE_Value* pThis,
2720                                       const ByteStringView& szFuncName,
2721                                       CFXJSE_Arguments& args) {
2722   if (args.GetLength() != 1) {
2723     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"HasValue");
2724     return;
2725   }
2726 
2727   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2728   if (!argOne->IsString()) {
2729     args.GetReturnValue()->SetInteger(argOne->IsNumber() ||
2730                                       argOne->IsBoolean());
2731     return;
2732   }
2733 
2734   ByteString valueStr = argOne->ToString();
2735   valueStr.TrimLeft();
2736   args.GetReturnValue()->SetInteger(!valueStr.IsEmpty());
2737 }
2738 
2739 // static
Oneof(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2740 void CFXJSE_FormCalcContext::Oneof(CFXJSE_Value* pThis,
2741                                    const ByteStringView& szFuncName,
2742                                    CFXJSE_Arguments& args) {
2743   if (args.GetLength() < 2) {
2744     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Oneof");
2745     return;
2746   }
2747 
2748   bool bFlags = false;
2749   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2750   std::vector<std::unique_ptr<CFXJSE_Value>> parameterValues;
2751   unfoldArgs(pThis, args, &parameterValues, 1);
2752   for (const auto& value : parameterValues) {
2753     if (simpleValueCompare(pThis, argOne.get(), value.get())) {
2754       bFlags = true;
2755       break;
2756     }
2757   }
2758 
2759   args.GetReturnValue()->SetInteger(bFlags);
2760 }
2761 
2762 // static
Within(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2763 void CFXJSE_FormCalcContext::Within(CFXJSE_Value* pThis,
2764                                     const ByteStringView& szFuncName,
2765                                     CFXJSE_Arguments& args) {
2766   if (args.GetLength() != 3) {
2767     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Within");
2768     return;
2769   }
2770 
2771   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
2772   if (argOne->IsNull()) {
2773     args.GetReturnValue()->SetUndefined();
2774     return;
2775   }
2776 
2777   std::unique_ptr<CFXJSE_Value> argLow = GetSimpleValue(pThis, args, 1);
2778   std::unique_ptr<CFXJSE_Value> argHigh = GetSimpleValue(pThis, args, 2);
2779   if (argOne->IsNumber()) {
2780     float oneNumber = ValueToFloat(pThis, argOne.get());
2781     float lowNumber = ValueToFloat(pThis, argLow.get());
2782     float heightNumber = ValueToFloat(pThis, argHigh.get());
2783     args.GetReturnValue()->SetInteger((oneNumber >= lowNumber) &&
2784                                       (oneNumber <= heightNumber));
2785     return;
2786   }
2787 
2788   ByteString oneString = ValueToUTF8String(argOne.get());
2789   ByteString lowString = ValueToUTF8String(argLow.get());
2790   ByteString heightString = ValueToUTF8String(argHigh.get());
2791   args.GetReturnValue()->SetInteger(
2792       (oneString.Compare(lowString.AsStringView()) >= 0) &&
2793       (oneString.Compare(heightString.AsStringView()) <= 0));
2794 }
2795 
2796 // static
If(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2797 void CFXJSE_FormCalcContext::If(CFXJSE_Value* pThis,
2798                                 const ByteStringView& szFuncName,
2799                                 CFXJSE_Arguments& args) {
2800   if (args.GetLength() != 3) {
2801     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"If");
2802     return;
2803   }
2804 
2805   args.GetReturnValue()->Assign(GetSimpleValue(pThis, args, 0)->ToBoolean()
2806                                     ? GetSimpleValue(pThis, args, 1).get()
2807                                     : GetSimpleValue(pThis, args, 2).get());
2808 }
2809 
2810 // static
Eval(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2811 void CFXJSE_FormCalcContext::Eval(CFXJSE_Value* pThis,
2812                                   const ByteStringView& szFuncName,
2813                                   CFXJSE_Arguments& args) {
2814   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2815   if (args.GetLength() != 1) {
2816     pContext->ThrowParamCountMismatchException(L"Eval");
2817     return;
2818   }
2819 
2820   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
2821   std::unique_ptr<CFXJSE_Value> scriptValue = GetSimpleValue(pThis, args, 0);
2822   ByteString utf8ScriptString = ValueToUTF8String(scriptValue.get());
2823   if (utf8ScriptString.IsEmpty()) {
2824     args.GetReturnValue()->SetNull();
2825     return;
2826   }
2827 
2828   CFX_WideTextBuf wsJavaScriptBuf;
2829   if (!CFXJSE_FormCalcContext::Translate(
2830           WideString::FromUTF8(utf8ScriptString.AsStringView()).AsStringView(),
2831           &wsJavaScriptBuf)) {
2832     pContext->ThrowCompilerErrorException();
2833     return;
2834   }
2835 
2836   std::unique_ptr<CFXJSE_Context> pNewContext(
2837       CFXJSE_Context::Create(pIsolate, nullptr, nullptr));
2838 
2839   auto returnValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
2840   pNewContext->ExecuteScript(
2841       FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).c_str(), returnValue.get());
2842 
2843   args.GetReturnValue()->Assign(returnValue.get());
2844 }
2845 
2846 // static
Ref(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2847 void CFXJSE_FormCalcContext::Ref(CFXJSE_Value* pThis,
2848                                  const ByteStringView& szFuncName,
2849                                  CFXJSE_Arguments& args) {
2850   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
2851   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
2852   if (args.GetLength() != 1) {
2853     pContext->ThrowParamCountMismatchException(L"Ref");
2854     return;
2855   }
2856 
2857   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
2858   if (!argOne->IsArray() && !argOne->IsObject() && !argOne->IsBoolean() &&
2859       !argOne->IsString() && !argOne->IsNull() && !argOne->IsNumber()) {
2860     pContext->ThrowArgumentMismatchException();
2861     return;
2862   }
2863 
2864   if (argOne->IsBoolean() || argOne->IsString() || argOne->IsNumber()) {
2865     args.GetReturnValue()->Assign(argOne.get());
2866     return;
2867   }
2868 
2869   std::vector<std::unique_ptr<CFXJSE_Value>> values;
2870   for (int32_t i = 0; i < 3; i++)
2871     values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
2872 
2873   int intVal = 3;
2874   if (argOne->IsNull()) {
2875     // TODO(dsinclair): Why is this 4 when the others are all 3?
2876     intVal = 4;
2877     values[2]->SetNull();
2878   } else if (argOne->IsArray()) {
2879 #ifndef NDEBUG
2880     auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
2881     argOne->GetObjectProperty("length", lengthValue.get());
2882     ASSERT(lengthValue->ToInteger() >= 3);
2883 #endif
2884 
2885     auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
2886     auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
2887     argOne->GetObjectPropertyByIdx(1, propertyValue.get());
2888     argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
2889     if (!propertyValue->IsNull() || jsObjectValue->IsNull()) {
2890       pContext->ThrowArgumentMismatchException();
2891       return;
2892     }
2893 
2894     values[2]->Assign(jsObjectValue.get());
2895   } else if (argOne->IsObject()) {
2896     values[2]->Assign(argOne.get());
2897   }
2898 
2899   values[0]->SetInteger(intVal);
2900   values[1]->SetNull();
2901   args.GetReturnValue()->SetArray(values);
2902 }
2903 
2904 // static
UnitType(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)2905 void CFXJSE_FormCalcContext::UnitType(CFXJSE_Value* pThis,
2906                                       const ByteStringView& szFuncName,
2907                                       CFXJSE_Arguments& args) {
2908   if (args.GetLength() != 1) {
2909     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"UnitType");
2910     return;
2911   }
2912 
2913   std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
2914   if (unitspanValue->IsNull()) {
2915     args.GetReturnValue()->SetNull();
2916     return;
2917   }
2918 
2919   ByteString unitspanString = ValueToUTF8String(unitspanValue.get());
2920   if (unitspanString.IsEmpty()) {
2921     args.GetReturnValue()->SetString("in");
2922     return;
2923   }
2924 
2925   enum XFA_FM2JS_VALUETYPE_ParserStatus {
2926     VALUETYPE_START,
2927     VALUETYPE_HAVEINVALIDCHAR,
2928     VALUETYPE_HAVEDIGIT,
2929     VALUETYPE_HAVEDIGITWHITE,
2930     VALUETYPE_ISCM,
2931     VALUETYPE_ISMM,
2932     VALUETYPE_ISPT,
2933     VALUETYPE_ISMP,
2934     VALUETYPE_ISIN,
2935   };
2936   unitspanString.MakeLower();
2937   WideString wsTypeString = WideString::FromUTF8(unitspanString.AsStringView());
2938   const wchar_t* pData = wsTypeString.c_str();
2939   int32_t u = 0;
2940   int32_t uLen = wsTypeString.GetLength();
2941   while (IsWhitespace(pData[u]))
2942     u++;
2943 
2944   XFA_FM2JS_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START;
2945   wchar_t typeChar;
2946   // TODO(dsinclair): Cleanup this parser, figure out what the various checks
2947   //    are for.
2948   while (u < uLen) {
2949     typeChar = pData[u];
2950     if (IsWhitespace(typeChar)) {
2951       if (eParserStatus != VALUETYPE_HAVEDIGIT &&
2952           eParserStatus != VALUETYPE_HAVEDIGITWHITE) {
2953         eParserStatus = VALUETYPE_ISIN;
2954         break;
2955       }
2956       eParserStatus = VALUETYPE_HAVEDIGITWHITE;
2957     } else if (IsPartOfNumberW(typeChar)) {
2958       if (eParserStatus == VALUETYPE_HAVEDIGITWHITE) {
2959         eParserStatus = VALUETYPE_ISIN;
2960         break;
2961       }
2962       eParserStatus = VALUETYPE_HAVEDIGIT;
2963     } else if ((typeChar == 'c' || typeChar == 'p') && (u + 1 < uLen)) {
2964       wchar_t nextChar = pData[u + 1];
2965       if ((eParserStatus == VALUETYPE_START ||
2966            eParserStatus == VALUETYPE_HAVEDIGIT ||
2967            eParserStatus == VALUETYPE_HAVEDIGITWHITE) &&
2968           !IsPartOfNumberW(nextChar)) {
2969         eParserStatus = (typeChar == 'c') ? VALUETYPE_ISCM : VALUETYPE_ISPT;
2970         break;
2971       }
2972       eParserStatus = VALUETYPE_HAVEINVALIDCHAR;
2973     } else if (typeChar == 'm' && (u + 1 < uLen)) {
2974       wchar_t nextChar = pData[u + 1];
2975       if ((eParserStatus == VALUETYPE_START ||
2976            eParserStatus == VALUETYPE_HAVEDIGIT ||
2977            eParserStatus == VALUETYPE_HAVEDIGITWHITE) &&
2978           !IsPartOfNumberW(nextChar)) {
2979         eParserStatus = VALUETYPE_ISMM;
2980         if (nextChar == 'p' || ((u + 5 < uLen) && pData[u + 1] == 'i' &&
2981                                 pData[u + 2] == 'l' && pData[u + 3] == 'l' &&
2982                                 pData[u + 4] == 'i' && pData[u + 5] == 'p')) {
2983           eParserStatus = VALUETYPE_ISMP;
2984         }
2985         break;
2986       }
2987     } else {
2988       eParserStatus = VALUETYPE_HAVEINVALIDCHAR;
2989     }
2990     u++;
2991   }
2992   switch (eParserStatus) {
2993     case VALUETYPE_ISCM:
2994       args.GetReturnValue()->SetString("cm");
2995       break;
2996     case VALUETYPE_ISMM:
2997       args.GetReturnValue()->SetString("mm");
2998       break;
2999     case VALUETYPE_ISPT:
3000       args.GetReturnValue()->SetString("pt");
3001       break;
3002     case VALUETYPE_ISMP:
3003       args.GetReturnValue()->SetString("mp");
3004       break;
3005     default:
3006       args.GetReturnValue()->SetString("in");
3007       break;
3008   }
3009 }
3010 
3011 // static
UnitValue(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3012 void CFXJSE_FormCalcContext::UnitValue(CFXJSE_Value* pThis,
3013                                        const ByteStringView& szFuncName,
3014                                        CFXJSE_Arguments& args) {
3015   int32_t argc = args.GetLength();
3016   if (argc < 1 || argc > 2) {
3017     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"UnitValue");
3018     return;
3019   }
3020 
3021   std::unique_ptr<CFXJSE_Value> unitspanValue = GetSimpleValue(pThis, args, 0);
3022   if (unitspanValue->IsNull()) {
3023     args.GetReturnValue()->SetNull();
3024     return;
3025   }
3026 
3027   ByteString unitspanString = ValueToUTF8String(unitspanValue.get());
3028   const char* pData = unitspanString.c_str();
3029   if (!pData) {
3030     args.GetReturnValue()->SetInteger(0);
3031     return;
3032   }
3033 
3034   size_t u = 0;
3035   while (IsWhitespace(pData[u]))
3036     ++u;
3037 
3038   while (u < unitspanString.GetLength()) {
3039     if (!IsPartOfNumber(pData[u]))
3040       break;
3041     ++u;
3042   }
3043 
3044   char* pTemp = nullptr;
3045   double dFirstNumber = strtod(pData, &pTemp);
3046   while (IsWhitespace(pData[u]))
3047     ++u;
3048 
3049   size_t uLen = unitspanString.GetLength();
3050   ByteString strFirstUnit;
3051   while (u < uLen) {
3052     if (pData[u] == ' ')
3053       break;
3054 
3055     strFirstUnit += pData[u];
3056     ++u;
3057   }
3058   strFirstUnit.MakeLower();
3059 
3060   ByteString strUnit;
3061   if (argc > 1) {
3062     std::unique_ptr<CFXJSE_Value> unitValue = GetSimpleValue(pThis, args, 1);
3063     ByteString unitTempString = ValueToUTF8String(unitValue.get());
3064     const char* pChar = unitTempString.c_str();
3065     size_t uVal = 0;
3066     while (IsWhitespace(pChar[uVal]))
3067       ++uVal;
3068 
3069     while (uVal < unitTempString.GetLength()) {
3070       if (!std::isdigit(pChar[uVal]) && pChar[uVal] != '.')
3071         break;
3072       ++uVal;
3073     }
3074     while (IsWhitespace(pChar[uVal]))
3075       ++uVal;
3076 
3077     size_t uValLen = unitTempString.GetLength();
3078     while (uVal < uValLen) {
3079       if (pChar[uVal] == ' ')
3080         break;
3081 
3082       strUnit += pChar[uVal];
3083       ++uVal;
3084     }
3085     strUnit.MakeLower();
3086   } else {
3087     strUnit = strFirstUnit;
3088   }
3089 
3090   double dResult = 0;
3091   if (strFirstUnit == "in" || strFirstUnit == "inches") {
3092     if (strUnit == "mm" || strUnit == "millimeters")
3093       dResult = dFirstNumber * 25.4;
3094     else if (strUnit == "cm" || strUnit == "centimeters")
3095       dResult = dFirstNumber * 2.54;
3096     else if (strUnit == "pt" || strUnit == "points")
3097       dResult = dFirstNumber / 72;
3098     else if (strUnit == "mp" || strUnit == "millipoints")
3099       dResult = dFirstNumber / 72000;
3100     else
3101       dResult = dFirstNumber;
3102   } else if (strFirstUnit == "mm" || strFirstUnit == "millimeters") {
3103     if (strUnit == "mm" || strUnit == "millimeters")
3104       dResult = dFirstNumber;
3105     else if (strUnit == "cm" || strUnit == "centimeters")
3106       dResult = dFirstNumber / 10;
3107     else if (strUnit == "pt" || strUnit == "points")
3108       dResult = dFirstNumber / 25.4 / 72;
3109     else if (strUnit == "mp" || strUnit == "millipoints")
3110       dResult = dFirstNumber / 25.4 / 72000;
3111     else
3112       dResult = dFirstNumber / 25.4;
3113   } else if (strFirstUnit == "cm" || strFirstUnit == "centimeters") {
3114     if (strUnit == "mm" || strUnit == "millimeters")
3115       dResult = dFirstNumber * 10;
3116     else if (strUnit == "cm" || strUnit == "centimeters")
3117       dResult = dFirstNumber;
3118     else if (strUnit == "pt" || strUnit == "points")
3119       dResult = dFirstNumber / 2.54 / 72;
3120     else if (strUnit == "mp" || strUnit == "millipoints")
3121       dResult = dFirstNumber / 2.54 / 72000;
3122     else
3123       dResult = dFirstNumber / 2.54;
3124   } else if (strFirstUnit == "pt" || strFirstUnit == "points") {
3125     if (strUnit == "mm" || strUnit == "millimeters")
3126       dResult = dFirstNumber / 72 * 25.4;
3127     else if (strUnit == "cm" || strUnit == "centimeters")
3128       dResult = dFirstNumber / 72 * 2.54;
3129     else if (strUnit == "pt" || strUnit == "points")
3130       dResult = dFirstNumber;
3131     else if (strUnit == "mp" || strUnit == "millipoints")
3132       dResult = dFirstNumber * 1000;
3133     else
3134       dResult = dFirstNumber / 72;
3135   } else if (strFirstUnit == "mp" || strFirstUnit == "millipoints") {
3136     if (strUnit == "mm" || strUnit == "millimeters")
3137       dResult = dFirstNumber / 72000 * 25.4;
3138     else if (strUnit == "cm" || strUnit == "centimeters")
3139       dResult = dFirstNumber / 72000 * 2.54;
3140     else if (strUnit == "pt" || strUnit == "points")
3141       dResult = dFirstNumber / 1000;
3142     else if (strUnit == "mp" || strUnit == "millipoints")
3143       dResult = dFirstNumber;
3144     else
3145       dResult = dFirstNumber / 72000;
3146   }
3147   args.GetReturnValue()->SetDouble(dResult);
3148 }
3149 
3150 // static
At(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3151 void CFXJSE_FormCalcContext::At(CFXJSE_Value* pThis,
3152                                 const ByteStringView& szFuncName,
3153                                 CFXJSE_Arguments& args) {
3154   if (args.GetLength() != 2) {
3155     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"At");
3156     return;
3157   }
3158 
3159   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3160   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
3161   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
3162     args.GetReturnValue()->SetNull();
3163     return;
3164   }
3165 
3166   ByteString stringTwo = ValueToUTF8String(argTwo.get());
3167   if (stringTwo.IsEmpty()) {
3168     args.GetReturnValue()->SetInteger(1);
3169     return;
3170   }
3171 
3172   ByteString stringOne = ValueToUTF8String(argOne.get());
3173   auto pos = stringOne.Find(stringTwo.AsStringView());
3174   args.GetReturnValue()->SetInteger(pos.has_value() ? pos.value() + 1 : 0);
3175 }
3176 
3177 // static
Concat(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3178 void CFXJSE_FormCalcContext::Concat(CFXJSE_Value* pThis,
3179                                     const ByteStringView& szFuncName,
3180                                     CFXJSE_Arguments& args) {
3181   int32_t argc = args.GetLength();
3182   if (argc < 1) {
3183     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Concat");
3184     return;
3185   }
3186 
3187   ByteString resultString;
3188   bool bAllNull = true;
3189   for (int32_t i = 0; i < argc; i++) {
3190     std::unique_ptr<CFXJSE_Value> value = GetSimpleValue(pThis, args, i);
3191     if (ValueIsNull(pThis, value.get()))
3192       continue;
3193 
3194     bAllNull = false;
3195     resultString += ValueToUTF8String(value.get());
3196   }
3197 
3198   if (bAllNull) {
3199     args.GetReturnValue()->SetNull();
3200     return;
3201   }
3202 
3203   args.GetReturnValue()->SetString(resultString.AsStringView());
3204 }
3205 
3206 // static
Decode(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3207 void CFXJSE_FormCalcContext::Decode(CFXJSE_Value* pThis,
3208                                     const ByteStringView& szFuncName,
3209                                     CFXJSE_Arguments& args) {
3210   int32_t argc = args.GetLength();
3211   if (argc < 1 || argc > 2) {
3212     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Decode");
3213     return;
3214   }
3215 
3216   if (argc == 1) {
3217     std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3218     if (ValueIsNull(pThis, argOne.get())) {
3219       args.GetReturnValue()->SetNull();
3220       return;
3221     }
3222 
3223     WideString decoded = DecodeURL(
3224         WideString::FromUTF8(ValueToUTF8String(argOne.get()).AsStringView()));
3225 
3226     args.GetReturnValue()->SetString(
3227         FX_UTF8Encode(decoded.AsStringView()).AsStringView());
3228     return;
3229   }
3230 
3231   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3232   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
3233   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
3234     args.GetReturnValue()->SetNull();
3235     return;
3236   }
3237 
3238   ByteString toDecodeString = ValueToUTF8String(argOne.get());
3239   ByteString identifyString = ValueToUTF8String(argTwo.get());
3240   WideString decoded;
3241 
3242   WideString toDecodeWideString =
3243       WideString::FromUTF8(toDecodeString.AsStringView());
3244 
3245   if (identifyString.EqualNoCase("html"))
3246     decoded = DecodeHTML(toDecodeWideString);
3247   else if (identifyString.EqualNoCase("xml"))
3248     decoded = DecodeXML(toDecodeWideString);
3249   else
3250     decoded = DecodeURL(toDecodeWideString);
3251 
3252   args.GetReturnValue()->SetString(
3253       FX_UTF8Encode(decoded.AsStringView()).AsStringView());
3254 }
3255 
3256 // static
DecodeURL(const WideString & wsURLString)3257 WideString CFXJSE_FormCalcContext::DecodeURL(const WideString& wsURLString) {
3258   const wchar_t* pData = wsURLString.c_str();
3259   size_t i = 0;
3260   CFX_WideTextBuf wsResultBuf;
3261   while (i < wsURLString.GetLength()) {
3262     wchar_t ch = pData[i];
3263     if ('%' != ch) {
3264       wsResultBuf.AppendChar(ch);
3265       ++i;
3266       continue;
3267     }
3268 
3269     wchar_t chTemp = 0;
3270     int32_t iCount = 0;
3271     while (iCount < 2) {
3272       ++i;
3273       ch = pData[i];
3274       if (ch <= '9' && ch >= '0') {
3275         // TODO(dsinclair): Premultiply and add rather then scale.
3276         chTemp += (ch - '0') * (!iCount ? 16 : 1);
3277       } else if (ch <= 'F' && ch >= 'A') {
3278         chTemp += (ch - 'A' + 10) * (!iCount ? 16 : 1);
3279       } else if (ch <= 'f' && ch >= 'a') {
3280         chTemp += (ch - 'a' + 10) * (!iCount ? 16 : 1);
3281       } else {
3282         return WideString();
3283       }
3284       ++iCount;
3285     }
3286     wsResultBuf.AppendChar(chTemp);
3287     ++i;
3288   }
3289   wsResultBuf.AppendChar(0);
3290   return wsResultBuf.MakeString();
3291 }
3292 
3293 // static
DecodeHTML(const WideString & wsHTMLString)3294 WideString CFXJSE_FormCalcContext::DecodeHTML(const WideString& wsHTMLString) {
3295   wchar_t strString[9];
3296   size_t iStrIndex = 0;
3297   size_t iLen = wsHTMLString.GetLength();
3298   size_t i = 0;
3299   int32_t iCode = 0;
3300   const wchar_t* pData = wsHTMLString.c_str();
3301   CFX_WideTextBuf wsResultBuf;
3302   while (i < iLen) {
3303     wchar_t ch = pData[i];
3304     if (ch != '&') {
3305       wsResultBuf.AppendChar(ch);
3306       ++i;
3307       continue;
3308     }
3309 
3310     ++i;
3311     ch = pData[i];
3312     if (ch == '#') {
3313       ++i;
3314       ch = pData[i];
3315       if (ch != 'x' && ch != 'X') {
3316         return WideString();
3317       }
3318 
3319       ++i;
3320       ch = pData[i];
3321       if ((ch >= '0' && ch <= '9') || (ch <= 'f' && ch >= 'a') ||
3322           (ch <= 'F' && ch >= 'A')) {
3323         while (ch != ';' && i < iLen) {
3324           if (ch >= '0' && ch <= '9') {
3325             iCode += ch - '0';
3326           } else if (ch <= 'f' && ch >= 'a') {
3327             iCode += ch - 'a' + 10;
3328           } else if (ch <= 'F' && ch >= 'A') {
3329             iCode += ch - 'A' + 10;
3330           } else {
3331             return WideString();
3332           }
3333           ++i;
3334           // TODO(dsinclair): Postmultiply seems wrong, start at zero
3335           //   and pre-multiply then can remove the post divide.
3336           iCode *= 16;
3337           ch = pData[i];
3338         }
3339         iCode /= 16;
3340       }
3341     } else {
3342       while (ch != ';' && i < iLen) {
3343         strString[iStrIndex++] = ch;
3344         ++i;
3345         ch = pData[i];
3346       }
3347       strString[iStrIndex] = 0;
3348     }
3349     uint32_t iData = 0;
3350     if (HTMLSTR2Code(strString, &iData)) {
3351       wsResultBuf.AppendChar((wchar_t)iData);
3352     } else {
3353       wsResultBuf.AppendChar(iCode);
3354     }
3355     iStrIndex = 0;
3356     strString[iStrIndex] = 0;
3357     ++i;
3358   }
3359   wsResultBuf.AppendChar(0);
3360 
3361   return wsResultBuf.MakeString();
3362 }
3363 
3364 // static
DecodeXML(const WideString & wsXMLString)3365 WideString CFXJSE_FormCalcContext::DecodeXML(const WideString& wsXMLString) {
3366   wchar_t strString[9];
3367   int32_t iStrIndex = 0;
3368   int32_t iLen = wsXMLString.GetLength();
3369   int32_t i = 0;
3370   int32_t iCode = 0;
3371   wchar_t ch = 0;
3372   const wchar_t* pData = wsXMLString.c_str();
3373   CFX_WideTextBuf wsResultBuf;
3374   while (i < iLen) {
3375     ch = pData[i];
3376     if (ch != '&') {
3377       wsResultBuf.AppendChar(ch);
3378       ++i;
3379       continue;
3380     }
3381 
3382     // TODO(dsinclair): This is very similar to DecodeHTML, can they be
3383     //   combined?
3384     ++i;
3385     ch = pData[i];
3386     if (ch == '#') {
3387       ++i;
3388       ch = pData[i];
3389       if (ch != 'x' && ch != 'X') {
3390         return WideString();
3391       }
3392 
3393       ++i;
3394       ch = pData[i];
3395       if ((ch >= '0' && ch <= '9') || (ch <= 'f' && ch >= 'a') ||
3396           (ch <= 'F' && ch >= 'A')) {
3397         while (ch != ';') {
3398           if (ch >= '0' && ch <= '9') {
3399             iCode += ch - '0';
3400           } else if (ch <= 'f' && ch >= 'a') {
3401             iCode += ch - 'a' + 10;
3402           } else if (ch <= 'F' && ch >= 'A') {
3403             iCode += ch - 'A' + 10;
3404           } else {
3405             return WideString();
3406           }
3407           ++i;
3408           iCode *= 16;
3409           ch = pData[i];
3410         }
3411         iCode /= 16;
3412       }
3413     } else {
3414       while (ch != ';' && i < iLen) {
3415         strString[iStrIndex++] = ch;
3416         ++i;
3417         ch = pData[i];
3418       }
3419       strString[iStrIndex] = 0;
3420     }
3421 
3422     const wchar_t* const strName[] = {L"quot", L"amp", L"apos", L"lt", L"gt"};
3423     int32_t iIndex = 0;
3424     while (iIndex < 5) {
3425       if (memcmp(strString, strName[iIndex], wcslen(strName[iIndex])) == 0) {
3426         break;
3427       }
3428       ++iIndex;
3429     }
3430     switch (iIndex) {
3431       case 0:
3432         wsResultBuf.AppendChar('"');
3433         break;
3434       case 1:
3435         wsResultBuf.AppendChar('&');
3436         break;
3437       case 2:
3438         wsResultBuf.AppendChar('\'');
3439         break;
3440       case 3:
3441         wsResultBuf.AppendChar('<');
3442         break;
3443       case 4:
3444         wsResultBuf.AppendChar('>');
3445         break;
3446       default:
3447         wsResultBuf.AppendChar(iCode);
3448         break;
3449     }
3450     iStrIndex = 0;
3451     strString[iStrIndex] = 0;
3452     ++i;
3453     iCode = 0;
3454   }
3455   wsResultBuf.AppendChar(0);
3456   return wsResultBuf.MakeString();
3457 }
3458 
3459 // static
Encode(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3460 void CFXJSE_FormCalcContext::Encode(CFXJSE_Value* pThis,
3461                                     const ByteStringView& szFuncName,
3462                                     CFXJSE_Arguments& args) {
3463   int32_t argc = args.GetLength();
3464   if (argc < 1 || argc > 2) {
3465     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Encode");
3466     return;
3467   }
3468 
3469   if (argc == 1) {
3470     std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3471     if (ValueIsNull(pThis, argOne.get())) {
3472       args.GetReturnValue()->SetNull();
3473       return;
3474     }
3475 
3476     WideString encoded = EncodeURL(ValueToUTF8String(argOne.get()));
3477     args.GetReturnValue()->SetString(
3478         FX_UTF8Encode(encoded.AsStringView()).AsStringView());
3479     return;
3480   }
3481 
3482   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3483   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
3484   if (ValueIsNull(pThis, argOne.get()) || ValueIsNull(pThis, argTwo.get())) {
3485     args.GetReturnValue()->SetNull();
3486     return;
3487   }
3488 
3489   ByteString toEncodeString = ValueToUTF8String(argOne.get());
3490   ByteString identifyString = ValueToUTF8String(argTwo.get());
3491   WideString encoded;
3492   if (identifyString.EqualNoCase("html"))
3493     encoded = EncodeHTML(toEncodeString);
3494   else if (identifyString.EqualNoCase("xml"))
3495     encoded = EncodeXML(toEncodeString);
3496   else
3497     encoded = EncodeURL(toEncodeString);
3498 
3499   args.GetReturnValue()->SetString(
3500       FX_UTF8Encode(encoded.AsStringView()).AsStringView());
3501 }
3502 
3503 // static
EncodeURL(const ByteString & szURLString)3504 WideString CFXJSE_FormCalcContext::EncodeURL(const ByteString& szURLString) {
3505   WideString wsURLString = WideString::FromUTF8(szURLString.AsStringView());
3506   CFX_WideTextBuf wsResultBuf;
3507   wchar_t strEncode[4];
3508   strEncode[0] = '%';
3509   strEncode[3] = 0;
3510   wchar_t strUnsafe[] = {' ', '<',  '>', '"', '#', '%', '{', '}',
3511                          '|', '\\', '^', '~', '[', ']', '`'};
3512   wchar_t strReserved[] = {';', '/', '?', ':', '@', '=', '&'};
3513   wchar_t strSpecial[] = {'$', '-', '+', '!', '*', '\'', '(', ')', ','};
3514   const wchar_t* strCode = L"0123456789abcdef";
3515   for (auto ch : wsURLString) {
3516     int32_t i = 0;
3517     int32_t iCount = FX_ArraySize(strUnsafe);
3518     while (i < iCount) {
3519       if (ch == strUnsafe[i]) {
3520         int32_t iIndex = ch / 16;
3521         strEncode[1] = strCode[iIndex];
3522         strEncode[2] = strCode[ch - iIndex * 16];
3523         wsResultBuf << strEncode;
3524         break;
3525       }
3526       ++i;
3527     }
3528     if (i < iCount)
3529       continue;
3530 
3531     i = 0;
3532     iCount = FX_ArraySize(strReserved);
3533     while (i < iCount) {
3534       if (ch == strReserved[i]) {
3535         int32_t iIndex = ch / 16;
3536         strEncode[1] = strCode[iIndex];
3537         strEncode[2] = strCode[ch - iIndex * 16];
3538         wsResultBuf << strEncode;
3539         break;
3540       }
3541       ++i;
3542     }
3543     if (i < iCount)
3544       continue;
3545 
3546     i = 0;
3547     iCount = FX_ArraySize(strSpecial);
3548     while (i < iCount) {
3549       if (ch == strSpecial[i]) {
3550         wsResultBuf.AppendChar(ch);
3551         break;
3552       }
3553       ++i;
3554     }
3555     if (i < iCount)
3556       continue;
3557 
3558     if ((ch >= 0x80 && ch <= 0xff) || ch <= 0x1f || ch == 0x7f) {
3559       int32_t iIndex = ch / 16;
3560       strEncode[1] = strCode[iIndex];
3561       strEncode[2] = strCode[ch - iIndex * 16];
3562       wsResultBuf << strEncode;
3563     } else if (ch >= 0x20 && ch <= 0x7e) {
3564       wsResultBuf.AppendChar(ch);
3565     } else {
3566       const wchar_t iRadix = 16;
3567       WideString strTmp;
3568       while (ch >= iRadix) {
3569         wchar_t tmp = strCode[ch % iRadix];
3570         ch /= iRadix;
3571         strTmp += tmp;
3572       }
3573       strTmp += strCode[ch];
3574       int32_t iLen = strTmp.GetLength();
3575       if (iLen < 2)
3576         break;
3577 
3578       int32_t iIndex = 0;
3579       if (iLen % 2 != 0) {
3580         strEncode[1] = '0';
3581         strEncode[2] = strTmp[iLen - 1];
3582         iIndex = iLen - 2;
3583       } else {
3584         strEncode[1] = strTmp[iLen - 1];
3585         strEncode[2] = strTmp[iLen - 2];
3586         iIndex = iLen - 3;
3587       }
3588       wsResultBuf << strEncode;
3589       while (iIndex > 0) {
3590         strEncode[1] = strTmp[iIndex];
3591         strEncode[2] = strTmp[iIndex - 1];
3592         iIndex -= 2;
3593         wsResultBuf << strEncode;
3594       }
3595     }
3596   }
3597   wsResultBuf.AppendChar(0);
3598   return wsResultBuf.MakeString();
3599 }
3600 
3601 // static
EncodeHTML(const ByteString & szHTMLString)3602 WideString CFXJSE_FormCalcContext::EncodeHTML(const ByteString& szHTMLString) {
3603   WideString wsHTMLString = WideString::FromUTF8(szHTMLString.AsStringView());
3604   const wchar_t* strCode = L"0123456789abcdef";
3605   wchar_t strEncode[9];
3606   strEncode[0] = '&';
3607   strEncode[1] = '#';
3608   strEncode[2] = 'x';
3609   strEncode[5] = ';';
3610   strEncode[6] = 0;
3611   strEncode[7] = ';';
3612   strEncode[8] = 0;
3613   CFX_WideTextBuf wsResultBuf;
3614   int32_t iLen = wsHTMLString.GetLength();
3615   int32_t i = 0;
3616   const wchar_t* pData = wsHTMLString.c_str();
3617   while (i < iLen) {
3618     uint32_t ch = pData[i];
3619     WideString htmlReserve;
3620     if (HTMLCode2STR(ch, &htmlReserve)) {
3621       wsResultBuf.AppendChar(L'&');
3622       wsResultBuf << htmlReserve;
3623       wsResultBuf.AppendChar(L';');
3624     } else if (ch >= 32 && ch <= 126) {
3625       wsResultBuf.AppendChar((wchar_t)ch);
3626     } else if (ch < 256) {
3627       int32_t iIndex = ch / 16;
3628       strEncode[3] = strCode[iIndex];
3629       strEncode[4] = strCode[ch - iIndex * 16];
3630       strEncode[5] = ';';
3631       strEncode[6] = 0;
3632       wsResultBuf << strEncode;
3633     } else {
3634       int32_t iBigByte = ch / 256;
3635       int32_t iLittleByte = ch % 256;
3636       strEncode[3] = strCode[iBigByte / 16];
3637       strEncode[4] = strCode[iBigByte % 16];
3638       strEncode[5] = strCode[iLittleByte / 16];
3639       strEncode[6] = strCode[iLittleByte % 16];
3640       wsResultBuf << strEncode;
3641     }
3642     ++i;
3643   }
3644   wsResultBuf.AppendChar(0);
3645   return wsResultBuf.MakeString();
3646 }
3647 
3648 // static
EncodeXML(const ByteString & szXMLString)3649 WideString CFXJSE_FormCalcContext::EncodeXML(const ByteString& szXMLString) {
3650   WideString wsXMLString = WideString::FromUTF8(szXMLString.AsStringView());
3651   CFX_WideTextBuf wsResultBuf;
3652   wchar_t strEncode[9];
3653   strEncode[0] = '&';
3654   strEncode[1] = '#';
3655   strEncode[2] = 'x';
3656   strEncode[5] = ';';
3657   strEncode[6] = 0;
3658   strEncode[7] = ';';
3659   strEncode[8] = 0;
3660   const wchar_t* strCode = L"0123456789abcdef";
3661   for (const auto& ch : wsXMLString) {
3662     switch (ch) {
3663       case '"':
3664         wsResultBuf.AppendChar('&');
3665         wsResultBuf << WideStringView(L"quot");
3666         wsResultBuf.AppendChar(';');
3667         break;
3668       case '&':
3669         wsResultBuf.AppendChar('&');
3670         wsResultBuf << WideStringView(L"amp");
3671         wsResultBuf.AppendChar(';');
3672         break;
3673       case '\'':
3674         wsResultBuf.AppendChar('&');
3675         wsResultBuf << WideStringView(L"apos");
3676         wsResultBuf.AppendChar(';');
3677         break;
3678       case '<':
3679         wsResultBuf.AppendChar('&');
3680         wsResultBuf << WideStringView(L"lt");
3681         wsResultBuf.AppendChar(';');
3682         break;
3683       case '>':
3684         wsResultBuf.AppendChar('&');
3685         wsResultBuf << WideStringView(L"gt");
3686         wsResultBuf.AppendChar(';');
3687         break;
3688       default: {
3689         if (ch >= 32 && ch <= 126) {
3690           wsResultBuf.AppendChar(ch);
3691         } else if (ch < 256) {
3692           int32_t iIndex = ch / 16;
3693           strEncode[3] = strCode[iIndex];
3694           strEncode[4] = strCode[ch - iIndex * 16];
3695           strEncode[5] = ';';
3696           strEncode[6] = 0;
3697           wsResultBuf << strEncode;
3698         } else {
3699           int32_t iBigByte = ch / 256;
3700           int32_t iLittleByte = ch % 256;
3701           strEncode[3] = strCode[iBigByte / 16];
3702           strEncode[4] = strCode[iBigByte % 16];
3703           strEncode[5] = strCode[iLittleByte / 16];
3704           strEncode[6] = strCode[iLittleByte % 16];
3705           wsResultBuf << strEncode;
3706         }
3707         break;
3708       }
3709     }
3710   }
3711   wsResultBuf.AppendChar(0);
3712   return wsResultBuf.MakeString();
3713 }
3714 
3715 // static
HTMLSTR2Code(const WideStringView & pData,uint32_t * iCode)3716 bool CFXJSE_FormCalcContext::HTMLSTR2Code(const WideStringView& pData,
3717                                           uint32_t* iCode) {
3718   auto cmpFunc = [](const XFA_FMHtmlReserveCode& iter,
3719                     const WideStringView& val) {
3720     // TODO(tsepez): check usage of c_str() below.
3721     return wcscmp(val.unterminated_c_str(), iter.m_htmlReserve) > 0;
3722   };
3723   const XFA_FMHtmlReserveCode* result =
3724       std::lower_bound(std::begin(reservesForDecode),
3725                        std::end(reservesForDecode), pData, cmpFunc);
3726   if (result != std::end(reservesForEncode) &&
3727       !wcscmp(pData.unterminated_c_str(), result->m_htmlReserve)) {
3728     *iCode = result->m_uCode;
3729     return true;
3730   }
3731   return false;
3732 }
3733 
3734 // static
HTMLCode2STR(uint32_t iCode,WideString * wsHTMLReserve)3735 bool CFXJSE_FormCalcContext::HTMLCode2STR(uint32_t iCode,
3736                                           WideString* wsHTMLReserve) {
3737   auto cmpFunc = [](const XFA_FMHtmlReserveCode iter, const uint32_t& val) {
3738     return iter.m_uCode < val;
3739   };
3740   const XFA_FMHtmlReserveCode* result =
3741       std::lower_bound(std::begin(reservesForEncode),
3742                        std::end(reservesForEncode), iCode, cmpFunc);
3743   if (result != std::end(reservesForEncode) && result->m_uCode == iCode) {
3744     *wsHTMLReserve = result->m_htmlReserve;
3745     return true;
3746   }
3747   return false;
3748 }
3749 
3750 // static
Format(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3751 void CFXJSE_FormCalcContext::Format(CFXJSE_Value* pThis,
3752                                     const ByteStringView& szFuncName,
3753                                     CFXJSE_Arguments& args) {
3754   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
3755   if (args.GetLength() < 2) {
3756     pContext->ThrowParamCountMismatchException(L"Format");
3757     return;
3758   }
3759 
3760   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3761   ByteString szPattern = ValueToUTF8String(argOne.get());
3762 
3763   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
3764   ByteString szValue = ValueToUTF8String(argTwo.get());
3765 
3766   CXFA_Document* pDoc = pContext->GetDocument();
3767   CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
3768   CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
3769   ASSERT(pThisNode);
3770 
3771   IFX_Locale* pLocale = pThisNode->GetLocale();
3772   uint32_t patternType;
3773   WideString wsPattern = WideString::FromUTF8(szPattern.AsStringView());
3774   WideString wsValue = WideString::FromUTF8(szValue.AsStringView());
3775   if (!PatternStringType(szPattern.AsStringView(), patternType)) {
3776     switch (patternType) {
3777       case XFA_VT_DATETIME: {
3778         auto iTChar = wsPattern.Find(L'T');
3779         if (!iTChar.has_value()) {
3780           args.GetReturnValue()->SetString("");
3781           return;
3782         }
3783         WideString wsDatePattern(L"date{");
3784         wsDatePattern += wsPattern.Left(iTChar.value()) + L"} ";
3785 
3786         WideString wsTimePattern(L"time{");
3787         wsTimePattern +=
3788             wsPattern.Right(wsPattern.GetLength() - (iTChar.value() + 1)) +
3789             L"}";
3790         wsPattern = wsDatePattern + wsTimePattern;
3791       } break;
3792       case XFA_VT_DATE: {
3793         wsPattern = L"date{" + wsPattern + L"}";
3794       } break;
3795       case XFA_VT_TIME: {
3796         wsPattern = L"time{" + wsPattern + L"}";
3797       } break;
3798       case XFA_VT_TEXT: {
3799         wsPattern = L"text{" + wsPattern + L"}";
3800       } break;
3801       case XFA_VT_FLOAT: {
3802         wsPattern = L"num{" + wsPattern + L"}";
3803       } break;
3804       default: {
3805         WideString wsTestPattern;
3806         wsTestPattern = L"num{" + wsPattern + L"}";
3807         CXFA_LocaleValue tempLocaleValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
3808                                          pLocale, pMgr);
3809         if (tempLocaleValue.IsValid()) {
3810           wsPattern = wsTestPattern;
3811           patternType = XFA_VT_FLOAT;
3812         } else {
3813           wsTestPattern = L"text{" + wsPattern + L"}";
3814           wsPattern = wsTestPattern;
3815           patternType = XFA_VT_TEXT;
3816         }
3817       } break;
3818     }
3819   }
3820   CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale, pMgr);
3821   WideString wsRet;
3822   if (!localeValue.FormatPatterns(wsRet, wsPattern, pLocale,
3823                                   XFA_VALUEPICTURE_Display)) {
3824     args.GetReturnValue()->SetString("");
3825     return;
3826   }
3827 
3828   args.GetReturnValue()->SetString(wsRet.UTF8Encode().AsStringView());
3829 }
3830 
3831 // static
Left(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3832 void CFXJSE_FormCalcContext::Left(CFXJSE_Value* pThis,
3833                                   const ByteStringView& szFuncName,
3834                                   CFXJSE_Arguments& args) {
3835   if (args.GetLength() != 2) {
3836     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Left");
3837     return;
3838   }
3839 
3840   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3841   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
3842   if ((ValueIsNull(pThis, argOne.get())) ||
3843       (ValueIsNull(pThis, argTwo.get()))) {
3844     args.GetReturnValue()->SetNull();
3845     return;
3846   }
3847 
3848   ByteString sourceString = ValueToUTF8String(argOne.get());
3849   int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
3850   args.GetReturnValue()->SetString(sourceString.Left(count).AsStringView());
3851 }
3852 
3853 // static
Len(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3854 void CFXJSE_FormCalcContext::Len(CFXJSE_Value* pThis,
3855                                  const ByteStringView& szFuncName,
3856                                  CFXJSE_Arguments& args) {
3857   if (args.GetLength() != 1) {
3858     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Len");
3859     return;
3860   }
3861 
3862   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3863   if (ValueIsNull(pThis, argOne.get())) {
3864     args.GetReturnValue()->SetNull();
3865     return;
3866   }
3867 
3868   ByteString sourceString = ValueToUTF8String(argOne.get());
3869   args.GetReturnValue()->SetInteger(sourceString.GetLength());
3870 }
3871 
3872 // static
Lower(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3873 void CFXJSE_FormCalcContext::Lower(CFXJSE_Value* pThis,
3874                                    const ByteStringView& szFuncName,
3875                                    CFXJSE_Arguments& args) {
3876   int32_t argc = args.GetLength();
3877   if (argc < 1 || argc > 2) {
3878     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Lower");
3879     return;
3880   }
3881 
3882   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3883   if (ValueIsNull(pThis, argOne.get())) {
3884     args.GetReturnValue()->SetNull();
3885     return;
3886   }
3887 
3888   CFX_WideTextBuf lowStringBuf;
3889   ByteString argString = ValueToUTF8String(argOne.get());
3890   WideString wsArgString = WideString::FromUTF8(argString.AsStringView());
3891   const wchar_t* pData = wsArgString.c_str();
3892   size_t i = 0;
3893   while (i < argString.GetLength()) {
3894     int32_t ch = pData[i];
3895     if ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0xC0 && ch <= 0xDE))
3896       ch += 32;
3897     else if (ch == 0x100 || ch == 0x102 || ch == 0x104)
3898       ch += 1;
3899 
3900     lowStringBuf.AppendChar(ch);
3901     ++i;
3902   }
3903   lowStringBuf.AppendChar(0);
3904 
3905   args.GetReturnValue()->SetString(
3906       FX_UTF8Encode(lowStringBuf.AsStringView()).AsStringView());
3907 }
3908 
3909 // static
Ltrim(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3910 void CFXJSE_FormCalcContext::Ltrim(CFXJSE_Value* pThis,
3911                                    const ByteStringView& szFuncName,
3912                                    CFXJSE_Arguments& args) {
3913   if (args.GetLength() != 1) {
3914     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Ltrim");
3915     return;
3916   }
3917 
3918   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3919   if (ValueIsNull(pThis, argOne.get())) {
3920     args.GetReturnValue()->SetNull();
3921     return;
3922   }
3923 
3924   ByteString sourceString = ValueToUTF8String(argOne.get());
3925   sourceString.TrimLeft();
3926   args.GetReturnValue()->SetString(sourceString.AsStringView());
3927 }
3928 
3929 // static
Parse(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)3930 void CFXJSE_FormCalcContext::Parse(CFXJSE_Value* pThis,
3931                                    const ByteStringView& szFuncName,
3932                                    CFXJSE_Arguments& args) {
3933   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
3934   if (args.GetLength() != 2) {
3935     pContext->ThrowParamCountMismatchException(L"Parse");
3936     return;
3937   }
3938 
3939   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
3940   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
3941   if (ValueIsNull(pThis, argTwo.get())) {
3942     args.GetReturnValue()->SetNull();
3943     return;
3944   }
3945 
3946   ByteString szPattern = ValueToUTF8String(argOne.get());
3947   ByteString szValue = ValueToUTF8String(argTwo.get());
3948   CXFA_Document* pDoc = pContext->GetDocument();
3949   CXFA_LocaleMgr* pMgr = pDoc->GetLocalMgr();
3950   CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject());
3951   ASSERT(pThisNode);
3952 
3953   IFX_Locale* pLocale = pThisNode->GetLocale();
3954   WideString wsPattern = WideString::FromUTF8(szPattern.AsStringView());
3955   WideString wsValue = WideString::FromUTF8(szValue.AsStringView());
3956   uint32_t patternType;
3957   if (PatternStringType(szPattern.AsStringView(), patternType)) {
3958     CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale,
3959                                  pMgr);
3960     if (!localeValue.IsValid()) {
3961       args.GetReturnValue()->SetString("");
3962       return;
3963     }
3964     args.GetReturnValue()->SetString(
3965         localeValue.GetValue().UTF8Encode().AsStringView());
3966     return;
3967   }
3968 
3969   switch (patternType) {
3970     case XFA_VT_DATETIME: {
3971       auto iTChar = wsPattern.Find(L'T');
3972       if (!iTChar.has_value()) {
3973         args.GetReturnValue()->SetString("");
3974         return;
3975       }
3976       WideString wsDatePattern(L"date{" + wsPattern.Left(iTChar.value()) +
3977                                L"} ");
3978       WideString wsTimePattern(
3979           L"time{" +
3980           wsPattern.Right(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}");
3981       wsPattern = wsDatePattern + wsTimePattern;
3982       CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale,
3983                                    pMgr);
3984       if (!localeValue.IsValid()) {
3985         args.GetReturnValue()->SetString("");
3986         return;
3987       }
3988       args.GetReturnValue()->SetString(
3989           localeValue.GetValue().UTF8Encode().AsStringView());
3990       return;
3991     }
3992     case XFA_VT_DATE: {
3993       wsPattern = L"date{" + wsPattern + L"}";
3994       CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale,
3995                                    pMgr);
3996       if (!localeValue.IsValid()) {
3997         args.GetReturnValue()->SetString("");
3998         return;
3999       }
4000       args.GetReturnValue()->SetString(
4001           localeValue.GetValue().UTF8Encode().AsStringView());
4002       return;
4003     }
4004     case XFA_VT_TIME: {
4005       wsPattern = L"time{" + wsPattern + L"}";
4006       CXFA_LocaleValue localeValue(patternType, wsValue, wsPattern, pLocale,
4007                                    pMgr);
4008       if (!localeValue.IsValid()) {
4009         args.GetReturnValue()->SetString("");
4010         return;
4011       }
4012       args.GetReturnValue()->SetString(
4013           localeValue.GetValue().UTF8Encode().AsStringView());
4014       return;
4015     }
4016     case XFA_VT_TEXT: {
4017       wsPattern = L"text{" + wsPattern + L"}";
4018       CXFA_LocaleValue localeValue(XFA_VT_TEXT, wsValue, wsPattern, pLocale,
4019                                    pMgr);
4020       if (!localeValue.IsValid()) {
4021         args.GetReturnValue()->SetString("");
4022         return;
4023       }
4024       args.GetReturnValue()->SetString(
4025           localeValue.GetValue().UTF8Encode().AsStringView());
4026       return;
4027     }
4028     case XFA_VT_FLOAT: {
4029       wsPattern = L"num{" + wsPattern + L"}";
4030       CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsPattern, pLocale,
4031                                    pMgr);
4032       if (!localeValue.IsValid()) {
4033         args.GetReturnValue()->SetString("");
4034         return;
4035       }
4036       args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
4037       return;
4038     }
4039     default: {
4040       WideString wsTestPattern;
4041       wsTestPattern = L"num{" + wsPattern + L"}";
4042       CXFA_LocaleValue localeValue(XFA_VT_FLOAT, wsValue, wsTestPattern,
4043                                    pLocale, pMgr);
4044       if (localeValue.IsValid()) {
4045         args.GetReturnValue()->SetDouble(localeValue.GetDoubleNum());
4046         return;
4047       }
4048 
4049       wsTestPattern = L"text{" + wsPattern + L"}";
4050       CXFA_LocaleValue localeValue2(XFA_VT_TEXT, wsValue, wsTestPattern,
4051                                     pLocale, pMgr);
4052       if (!localeValue2.IsValid()) {
4053         args.GetReturnValue()->SetString("");
4054         return;
4055       }
4056       args.GetReturnValue()->SetString(
4057           localeValue2.GetValue().UTF8Encode().AsStringView());
4058       return;
4059     }
4060   }
4061 }
4062 
4063 // static
Replace(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4064 void CFXJSE_FormCalcContext::Replace(CFXJSE_Value* pThis,
4065                                      const ByteStringView& szFuncName,
4066                                      CFXJSE_Arguments& args) {
4067   int32_t argc = args.GetLength();
4068   if (argc < 2 || argc > 3) {
4069     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Replace");
4070     return;
4071   }
4072 
4073   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
4074   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
4075   ByteString oneString;
4076   ByteString twoString;
4077   if (!ValueIsNull(pThis, argOne.get()) && !ValueIsNull(pThis, argTwo.get())) {
4078     oneString = ValueToUTF8String(argOne.get());
4079     twoString = ValueToUTF8String(argTwo.get());
4080   }
4081 
4082   ByteString threeString;
4083   if (argc > 2) {
4084     std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
4085     threeString = ValueToUTF8String(argThree.get());
4086   }
4087 
4088   size_t iFindLen = twoString.GetLength();
4089   std::ostringstream resultString;
4090   size_t iFindIndex = 0;
4091   for (size_t u = 0; u < oneString.GetLength(); ++u) {
4092     char ch = static_cast<char>(oneString[u]);
4093     if (ch != static_cast<char>(twoString[iFindIndex])) {
4094       resultString << ch;
4095       continue;
4096     }
4097 
4098     size_t iTemp = u + 1;
4099     ++iFindIndex;
4100     while (iFindIndex < iFindLen) {
4101       uint8_t chTemp = oneString[iTemp];
4102       if (chTemp != twoString[iFindIndex]) {
4103         iFindIndex = 0;
4104         break;
4105       }
4106 
4107       ++iTemp;
4108       ++iFindIndex;
4109     }
4110     if (iFindIndex == iFindLen) {
4111       resultString << threeString;
4112       u += iFindLen - 1;
4113       iFindIndex = 0;
4114     } else {
4115       resultString << ch;
4116     }
4117   }
4118   resultString << '\0';
4119   args.GetReturnValue()->SetString(ByteStringView(resultString.str().c_str()));
4120 }
4121 
4122 // static
Right(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4123 void CFXJSE_FormCalcContext::Right(CFXJSE_Value* pThis,
4124                                    const ByteStringView& szFuncName,
4125                                    CFXJSE_Arguments& args) {
4126   if (args.GetLength() != 2) {
4127     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Right");
4128     return;
4129   }
4130 
4131   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
4132   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
4133   if ((ValueIsNull(pThis, argOne.get())) ||
4134       (ValueIsNull(pThis, argTwo.get()))) {
4135     args.GetReturnValue()->SetNull();
4136     return;
4137   }
4138 
4139   ByteString sourceString = ValueToUTF8String(argOne.get());
4140   int32_t count = std::max(0, ValueToInteger(pThis, argTwo.get()));
4141   args.GetReturnValue()->SetString(sourceString.Right(count).AsStringView());
4142 }
4143 
4144 // static
Rtrim(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4145 void CFXJSE_FormCalcContext::Rtrim(CFXJSE_Value* pThis,
4146                                    const ByteStringView& szFuncName,
4147                                    CFXJSE_Arguments& args) {
4148   if (args.GetLength() != 1) {
4149     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Rtrim");
4150     return;
4151   }
4152 
4153   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
4154   if (ValueIsNull(pThis, argOne.get())) {
4155     args.GetReturnValue()->SetNull();
4156     return;
4157   }
4158 
4159   ByteString sourceString = ValueToUTF8String(argOne.get());
4160   sourceString.TrimRight();
4161   args.GetReturnValue()->SetString(sourceString.AsStringView());
4162 }
4163 
4164 // static
Space(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4165 void CFXJSE_FormCalcContext::Space(CFXJSE_Value* pThis,
4166                                    const ByteStringView& szFuncName,
4167                                    CFXJSE_Arguments& args) {
4168   if (args.GetLength() != 1) {
4169     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Space");
4170     return;
4171   }
4172 
4173   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
4174   if (argOne->IsNull()) {
4175     args.GetReturnValue()->SetNull();
4176     return;
4177   }
4178 
4179   int32_t count = std::max(0, ValueToInteger(pThis, argOne.get()));
4180   std::ostringstream spaceString;
4181   int32_t index = 0;
4182   while (index < count) {
4183     spaceString << ' ';
4184     index++;
4185   }
4186   spaceString << '\0';
4187   args.GetReturnValue()->SetString(ByteStringView(spaceString.str().c_str()));
4188 }
4189 
4190 // static
Str(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4191 void CFXJSE_FormCalcContext::Str(CFXJSE_Value* pThis,
4192                                  const ByteStringView& szFuncName,
4193                                  CFXJSE_Arguments& args) {
4194   int32_t argc = args.GetLength();
4195   if (argc < 1 || argc > 3) {
4196     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Str");
4197     return;
4198   }
4199 
4200   std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
4201   if (numberValue->IsNull()) {
4202     args.GetReturnValue()->SetNull();
4203     return;
4204   }
4205   float fNumber = ValueToFloat(pThis, numberValue.get());
4206 
4207   int32_t iWidth = 10;
4208   if (argc > 1) {
4209     std::unique_ptr<CFXJSE_Value> widthValue = GetSimpleValue(pThis, args, 1);
4210     iWidth = static_cast<int32_t>(ValueToFloat(pThis, widthValue.get()));
4211   }
4212 
4213   int32_t iPrecision = 0;
4214   if (argc > 2) {
4215     std::unique_ptr<CFXJSE_Value> precisionValue =
4216         GetSimpleValue(pThis, args, 2);
4217     iPrecision = std::max(
4218         0, static_cast<int32_t>(ValueToFloat(pThis, precisionValue.get())));
4219   }
4220 
4221   ByteString formatStr = "%";
4222   if (iPrecision) {
4223     formatStr += ".";
4224     formatStr += ByteString::FormatInteger(iPrecision);
4225   }
4226   formatStr += "f";
4227   ByteString numberString = ByteString::Format(formatStr.c_str(), fNumber);
4228 
4229   const char* pData = numberString.c_str();
4230   int32_t iLength = numberString.GetLength();
4231   int32_t u = 0;
4232   while (u < iLength) {
4233     if (pData[u] == '.')
4234       break;
4235 
4236     ++u;
4237   }
4238 
4239   std::ostringstream resultBuf;
4240   if (u > iWidth || (iPrecision + u) >= iWidth) {
4241     int32_t i = 0;
4242     while (i < iWidth) {
4243       resultBuf << '*';
4244       ++i;
4245     }
4246     resultBuf << '\0';
4247     args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
4248     return;
4249   }
4250 
4251   if (u == iLength) {
4252     if (iLength > iWidth) {
4253       int32_t i = 0;
4254       while (i < iWidth) {
4255         resultBuf << '*';
4256         ++i;
4257       }
4258     } else {
4259       int32_t i = 0;
4260       while (i < iWidth - iLength) {
4261         resultBuf << ' ';
4262         ++i;
4263       }
4264       resultBuf << pData;
4265     }
4266     args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
4267     return;
4268   }
4269 
4270   int32_t iLeavingSpace = iWidth - u - iPrecision;
4271   if (iPrecision != 0)
4272     iLeavingSpace--;
4273 
4274   int32_t i = 0;
4275   while (i < iLeavingSpace) {
4276     resultBuf << ' ';
4277     ++i;
4278   }
4279   i = 0;
4280   while (i < u) {
4281     resultBuf << pData[i];
4282     ++i;
4283   }
4284   if (iPrecision != 0)
4285     resultBuf << '.';
4286 
4287   u++;
4288   i = 0;
4289   while (u < iLength) {
4290     if (i >= iPrecision)
4291       break;
4292 
4293     resultBuf << pData[u];
4294     ++i;
4295     ++u;
4296   }
4297   while (i < iPrecision) {
4298     resultBuf << '0';
4299     ++i;
4300   }
4301   resultBuf << '\0';
4302   args.GetReturnValue()->SetString(ByteStringView(resultBuf.str().c_str()));
4303 }
4304 
4305 // static
Stuff(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4306 void CFXJSE_FormCalcContext::Stuff(CFXJSE_Value* pThis,
4307                                    const ByteStringView& szFuncName,
4308                                    CFXJSE_Arguments& args) {
4309   int32_t argc = args.GetLength();
4310   if (argc < 3 || argc > 4) {
4311     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Stuff");
4312     return;
4313   }
4314 
4315   ByteString sourceString;
4316   ByteString insertString;
4317   int32_t iLength = 0;
4318   int32_t iStart = 0;
4319   int32_t iDelete = 0;
4320   std::unique_ptr<CFXJSE_Value> sourceValue = GetSimpleValue(pThis, args, 0);
4321   std::unique_ptr<CFXJSE_Value> startValue = GetSimpleValue(pThis, args, 1);
4322   std::unique_ptr<CFXJSE_Value> deleteValue = GetSimpleValue(pThis, args, 2);
4323   if (!sourceValue->IsNull() && !startValue->IsNull() &&
4324       !deleteValue->IsNull()) {
4325     sourceString = ValueToUTF8String(sourceValue.get());
4326     iLength = sourceString.GetLength();
4327     iStart = pdfium::clamp(
4328         static_cast<int32_t>(ValueToFloat(pThis, startValue.get())), 1,
4329         iLength);
4330     iDelete = std::max(
4331         0, static_cast<int32_t>(ValueToFloat(pThis, deleteValue.get())));
4332   }
4333 
4334   if (argc > 3) {
4335     std::unique_ptr<CFXJSE_Value> insertValue = GetSimpleValue(pThis, args, 3);
4336     insertString = ValueToUTF8String(insertValue.get());
4337   }
4338 
4339   iStart -= 1;
4340   std::ostringstream resultString;
4341   int32_t i = 0;
4342   while (i < iStart) {
4343     resultString << static_cast<char>(sourceString[i]);
4344     ++i;
4345   }
4346   resultString << insertString.AsStringView();
4347   i = iStart + iDelete;
4348   while (i < iLength) {
4349     resultString << static_cast<char>(sourceString[i]);
4350     ++i;
4351   }
4352   resultString << '\0';
4353   args.GetReturnValue()->SetString(ByteStringView(resultString.str().c_str()));
4354 }
4355 
4356 // static
Substr(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4357 void CFXJSE_FormCalcContext::Substr(CFXJSE_Value* pThis,
4358                                     const ByteStringView& szFuncName,
4359                                     CFXJSE_Arguments& args) {
4360   if (args.GetLength() != 3) {
4361     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Substr");
4362     return;
4363   }
4364 
4365   std::unique_ptr<CFXJSE_Value> stringValue = GetSimpleValue(pThis, args, 0);
4366   std::unique_ptr<CFXJSE_Value> startValue = GetSimpleValue(pThis, args, 1);
4367   std::unique_ptr<CFXJSE_Value> endValue = GetSimpleValue(pThis, args, 2);
4368   if (ValueIsNull(pThis, stringValue.get()) ||
4369       (ValueIsNull(pThis, startValue.get())) ||
4370       (ValueIsNull(pThis, endValue.get()))) {
4371     args.GetReturnValue()->SetNull();
4372     return;
4373   }
4374 
4375   int32_t iStart = 0;
4376   int32_t iCount = 0;
4377   ByteString szSourceStr = ValueToUTF8String(stringValue.get());
4378   int32_t iLength = szSourceStr.GetLength();
4379   if (iLength == 0) {
4380     args.GetReturnValue()->SetString("");
4381     return;
4382   }
4383 
4384   iStart = pdfium::clamp(
4385       iLength, 1, static_cast<int32_t>(ValueToFloat(pThis, startValue.get())));
4386   iCount =
4387       std::max(0, static_cast<int32_t>(ValueToFloat(pThis, endValue.get())));
4388 
4389   iStart -= 1;
4390   args.GetReturnValue()->SetString(
4391       szSourceStr.Mid(iStart, iCount).AsStringView());
4392 }
4393 
4394 // static
Uuid(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4395 void CFXJSE_FormCalcContext::Uuid(CFXJSE_Value* pThis,
4396                                   const ByteStringView& szFuncName,
4397                                   CFXJSE_Arguments& args) {
4398   int32_t argc = args.GetLength();
4399   if (argc < 0 || argc > 1) {
4400     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Uuid");
4401     return;
4402   }
4403 
4404   int32_t iNum = 0;
4405   if (argc > 0) {
4406     std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
4407     iNum = static_cast<int32_t>(ValueToFloat(pThis, argOne.get()));
4408   }
4409   args.GetReturnValue()->SetString(GUIDString(!!iNum).AsStringView());
4410 }
4411 
4412 // static
Upper(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4413 void CFXJSE_FormCalcContext::Upper(CFXJSE_Value* pThis,
4414                                    const ByteStringView& szFuncName,
4415                                    CFXJSE_Arguments& args) {
4416   int32_t argc = args.GetLength();
4417   if (argc < 1 || argc > 2) {
4418     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"Upper");
4419     return;
4420   }
4421 
4422   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
4423   if (ValueIsNull(pThis, argOne.get())) {
4424     args.GetReturnValue()->SetNull();
4425     return;
4426   }
4427 
4428   CFX_WideTextBuf upperStringBuf;
4429   ByteString argString = ValueToUTF8String(argOne.get());
4430   WideString wsArgString = WideString::FromUTF8(argString.AsStringView());
4431   const wchar_t* pData = wsArgString.c_str();
4432   size_t i = 0;
4433   while (i < wsArgString.GetLength()) {
4434     int32_t ch = pData[i];
4435     if ((ch >= 0x61 && ch <= 0x7A) || (ch >= 0xE0 && ch <= 0xFE))
4436       ch -= 32;
4437     else if (ch == 0x101 || ch == 0x103 || ch == 0x105)
4438       ch -= 1;
4439 
4440     upperStringBuf.AppendChar(ch);
4441     ++i;
4442   }
4443   upperStringBuf.AppendChar(0);
4444 
4445   args.GetReturnValue()->SetString(
4446       FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView());
4447 }
4448 
4449 // static
WordNum(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4450 void CFXJSE_FormCalcContext::WordNum(CFXJSE_Value* pThis,
4451                                      const ByteStringView& szFuncName,
4452                                      CFXJSE_Arguments& args) {
4453   int32_t argc = args.GetLength();
4454   if (argc < 1 || argc > 3) {
4455     ToJSContext(pThis, nullptr)->ThrowParamCountMismatchException(L"WordNum");
4456     return;
4457   }
4458 
4459   std::unique_ptr<CFXJSE_Value> numberValue = GetSimpleValue(pThis, args, 0);
4460   if (numberValue->IsNull()) {
4461     args.GetReturnValue()->SetNull();
4462     return;
4463   }
4464   float fNumber = ValueToFloat(pThis, numberValue.get());
4465 
4466   int32_t iIdentifier = 0;
4467   if (argc > 1) {
4468     std::unique_ptr<CFXJSE_Value> identifierValue =
4469         GetSimpleValue(pThis, args, 1);
4470     if (identifierValue->IsNull()) {
4471       args.GetReturnValue()->SetNull();
4472       return;
4473     }
4474     iIdentifier =
4475         static_cast<int32_t>(ValueToFloat(pThis, identifierValue.get()));
4476   }
4477 
4478   ByteString localeString;
4479   if (argc > 2) {
4480     std::unique_ptr<CFXJSE_Value> localeValue = GetSimpleValue(pThis, args, 2);
4481     if (localeValue->IsNull()) {
4482       args.GetReturnValue()->SetNull();
4483       return;
4484     }
4485     localeString = ValueToUTF8String(localeValue.get());
4486   }
4487 
4488   if (fNumber < 0.0f || fNumber > 922337203685477550.0f) {
4489     args.GetReturnValue()->SetString("*");
4490     return;
4491   }
4492 
4493   args.GetReturnValue()->SetString(
4494       WordUS(ByteString::Format("%.2f", fNumber), iIdentifier).AsStringView());
4495 }
4496 
4497 // static
TrillionUS(const ByteStringView & szData)4498 ByteString CFXJSE_FormCalcContext::TrillionUS(const ByteStringView& szData) {
4499   std::ostringstream strBuf;
4500   ByteStringView pUnits[] = {"zero", "one", "two",   "three", "four",
4501                              "five", "six", "seven", "eight", "nine"};
4502   ByteStringView pCapUnits[] = {"Zero", "One", "Two",   "Three", "Four",
4503                                 "Five", "Six", "Seven", "Eight", "Nine"};
4504   ByteStringView pTens[] = {"Ten",      "Eleven",  "Twelve",  "Thirteen",
4505                             "Fourteen", "Fifteen", "Sixteen", "Seventeen",
4506                             "Eighteen", "Nineteen"};
4507   ByteStringView pLastTens[] = {"Twenty", "Thirty",  "Forty",  "Fifty",
4508                                 "Sixty",  "Seventy", "Eighty", "Ninety"};
4509   ByteStringView pComm[] = {" Hundred ", " Thousand ", " Million ", " Billion ",
4510                             "Trillion"};
4511   const char* pData = szData.unterminated_c_str();
4512   int32_t iLength = szData.GetLength();
4513   int32_t iComm = 0;
4514   if (iLength > 12)
4515     iComm = 4;
4516   else if (iLength > 9)
4517     iComm = 3;
4518   else if (iLength > 6)
4519     iComm = 2;
4520   else if (iLength > 3)
4521     iComm = 1;
4522 
4523   int32_t iFirstCount = iLength % 3;
4524   if (iFirstCount == 0)
4525     iFirstCount = 3;
4526 
4527   int32_t iIndex = 0;
4528   if (iFirstCount == 3) {
4529     if (pData[iIndex] != '0') {
4530       strBuf << pCapUnits[pData[iIndex] - '0'];
4531       strBuf << pComm[0];
4532     }
4533     if (pData[iIndex + 1] == '0') {
4534       strBuf << pCapUnits[pData[iIndex + 2] - '0'];
4535     } else {
4536       if (pData[iIndex + 1] > '1') {
4537         strBuf << pLastTens[pData[iIndex + 1] - '2'];
4538         strBuf << "-";
4539         strBuf << pUnits[pData[iIndex + 2] - '0'];
4540       } else if (pData[iIndex + 1] == '1') {
4541         strBuf << pTens[pData[iIndex + 2] - '0'];
4542       } else if (pData[iIndex + 1] == '0') {
4543         strBuf << pCapUnits[pData[iIndex + 2] - '0'];
4544       }
4545     }
4546     iIndex += 3;
4547   } else if (iFirstCount == 2) {
4548     if (pData[iIndex] == '0') {
4549       strBuf << pCapUnits[pData[iIndex + 1] - '0'];
4550     } else {
4551       if (pData[iIndex] > '1') {
4552         strBuf << pLastTens[pData[iIndex] - '2'];
4553         strBuf << "-";
4554         strBuf << pUnits[pData[iIndex + 1] - '0'];
4555       } else if (pData[iIndex] == '1') {
4556         strBuf << pTens[pData[iIndex + 1] - '0'];
4557       } else if (pData[iIndex] == '0') {
4558         strBuf << pCapUnits[pData[iIndex + 1] - '0'];
4559       }
4560     }
4561     iIndex += 2;
4562   } else if (iFirstCount == 1) {
4563     strBuf << pCapUnits[pData[iIndex] - '0'];
4564     iIndex += 1;
4565   }
4566   if (iLength > 3 && iFirstCount > 0) {
4567     strBuf << pComm[iComm];
4568     --iComm;
4569   }
4570   while (iIndex < iLength) {
4571     if (pData[iIndex] != '0') {
4572       strBuf << pCapUnits[pData[iIndex] - '0'];
4573       strBuf << pComm[0];
4574     }
4575     if (pData[iIndex + 1] == '0') {
4576       strBuf << pCapUnits[pData[iIndex + 2] - '0'];
4577     } else {
4578       if (pData[iIndex + 1] > '1') {
4579         strBuf << pLastTens[pData[iIndex + 1] - '2'];
4580         strBuf << "-";
4581         strBuf << pUnits[pData[iIndex + 2] - '0'];
4582       } else if (pData[iIndex + 1] == '1') {
4583         strBuf << pTens[pData[iIndex + 2] - '0'];
4584       } else if (pData[iIndex + 1] == '0') {
4585         strBuf << pCapUnits[pData[iIndex + 2] - '0'];
4586       }
4587     }
4588     if (iIndex < iLength - 3) {
4589       strBuf << pComm[iComm];
4590       --iComm;
4591     }
4592     iIndex += 3;
4593   }
4594   return ByteString(strBuf);
4595 }
4596 
4597 // static
WordUS(const ByteString & szData,int32_t iStyle)4598 ByteString CFXJSE_FormCalcContext::WordUS(const ByteString& szData,
4599                                           int32_t iStyle) {
4600   const char* pData = szData.c_str();
4601   int32_t iLength = szData.GetLength();
4602   if (iStyle < 0 || iStyle > 2) {
4603     return ByteString();
4604   }
4605 
4606   std::ostringstream strBuf;
4607 
4608   int32_t iIndex = 0;
4609   while (iIndex < iLength) {
4610     if (pData[iIndex] == '.')
4611       break;
4612     ++iIndex;
4613   }
4614   int32_t iInteger = iIndex;
4615   iIndex = 0;
4616   while (iIndex < iInteger) {
4617     int32_t iCount = (iInteger - iIndex) % 12;
4618     if (!iCount && iInteger - iIndex > 0)
4619       iCount = 12;
4620 
4621     strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
4622     iIndex += iCount;
4623     if (iIndex < iInteger)
4624       strBuf << " Trillion ";
4625   }
4626 
4627   if (iStyle > 0)
4628     strBuf << " Dollars";
4629 
4630   if (iStyle > 1 && iInteger < iLength) {
4631     strBuf << " And ";
4632     iIndex = iInteger + 1;
4633     while (iIndex < iLength) {
4634       int32_t iCount = (iLength - iIndex) % 12;
4635       if (!iCount && iLength - iIndex > 0)
4636         iCount = 12;
4637 
4638       strBuf << TrillionUS(ByteStringView(pData + iIndex, iCount));
4639       iIndex += iCount;
4640       if (iIndex < iLength)
4641         strBuf << " Trillion ";
4642     }
4643     strBuf << " Cents";
4644   }
4645   return ByteString(strBuf);
4646 }
4647 
4648 // static
Get(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4649 void CFXJSE_FormCalcContext::Get(CFXJSE_Value* pThis,
4650                                  const ByteStringView& szFuncName,
4651                                  CFXJSE_Arguments& args) {
4652   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
4653   if (args.GetLength() != 1) {
4654     pContext->ThrowParamCountMismatchException(L"Get");
4655     return;
4656   }
4657 
4658   CXFA_Document* pDoc = pContext->GetDocument();
4659   if (!pDoc)
4660     return;
4661 
4662   IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
4663   if (!pAppProvider)
4664     return;
4665 
4666   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
4667   ByteString urlString = ValueToUTF8String(argOne.get());
4668   RetainPtr<IFX_SeekableReadStream> pFile =
4669       pAppProvider->DownloadURL(WideString::FromUTF8(urlString.AsStringView()));
4670   if (!pFile)
4671     return;
4672 
4673   int32_t size = pFile->GetSize();
4674   std::vector<uint8_t> dataBuf(size);
4675   pFile->ReadBlock(dataBuf.data(), size);
4676   args.GetReturnValue()->SetString(ByteStringView(dataBuf));
4677 }
4678 
4679 // static
Post(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4680 void CFXJSE_FormCalcContext::Post(CFXJSE_Value* pThis,
4681                                   const ByteStringView& szFuncName,
4682                                   CFXJSE_Arguments& args) {
4683   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
4684   int32_t argc = args.GetLength();
4685   if (argc < 2 || argc > 5) {
4686     pContext->ThrowParamCountMismatchException(L"Post");
4687     return;
4688   }
4689 
4690   CXFA_Document* pDoc = pContext->GetDocument();
4691   if (!pDoc)
4692     return;
4693 
4694   IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
4695   if (!pAppProvider)
4696     return;
4697 
4698   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
4699   ByteString bsURL = ValueToUTF8String(argOne.get());
4700 
4701   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
4702   ByteString bsData = ValueToUTF8String(argTwo.get());
4703 
4704   ByteString bsContentType;
4705   if (argc > 2) {
4706     std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
4707     bsContentType = ValueToUTF8String(argThree.get());
4708   }
4709 
4710   ByteString bsEncode;
4711   if (argc > 3) {
4712     std::unique_ptr<CFXJSE_Value> argFour = GetSimpleValue(pThis, args, 3);
4713     bsEncode = ValueToUTF8String(argFour.get());
4714   }
4715 
4716   ByteString bsHeader;
4717   if (argc > 4) {
4718     std::unique_ptr<CFXJSE_Value> argFive = GetSimpleValue(pThis, args, 4);
4719     bsHeader = ValueToUTF8String(argFive.get());
4720   }
4721 
4722   WideString decodedResponse;
4723   if (!pAppProvider->PostRequestURL(
4724           WideString::FromUTF8(bsURL.AsStringView()),
4725           WideString::FromUTF8(bsData.AsStringView()),
4726           WideString::FromUTF8(bsContentType.AsStringView()),
4727           WideString::FromUTF8(bsEncode.AsStringView()),
4728           WideString::FromUTF8(bsHeader.AsStringView()), decodedResponse)) {
4729     pContext->ThrowServerDeniedException();
4730     return;
4731   }
4732   args.GetReturnValue()->SetString(decodedResponse.UTF8Encode().AsStringView());
4733 }
4734 
4735 // static
Put(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4736 void CFXJSE_FormCalcContext::Put(CFXJSE_Value* pThis,
4737                                  const ByteStringView& szFuncName,
4738                                  CFXJSE_Arguments& args) {
4739   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
4740   int32_t argc = args.GetLength();
4741   if (argc < 2 || argc > 3) {
4742     pContext->ThrowParamCountMismatchException(L"Put");
4743     return;
4744   }
4745 
4746   CXFA_Document* pDoc = pContext->GetDocument();
4747   if (!pDoc)
4748     return;
4749 
4750   IXFA_AppProvider* pAppProvider = pDoc->GetNotify()->GetAppProvider();
4751   if (!pAppProvider)
4752     return;
4753 
4754   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
4755   ByteString bsURL = ValueToUTF8String(argOne.get());
4756 
4757   std::unique_ptr<CFXJSE_Value> argTwo = GetSimpleValue(pThis, args, 1);
4758   ByteString bsData = ValueToUTF8String(argTwo.get());
4759 
4760   ByteString bsEncode;
4761   if (argc > 2) {
4762     std::unique_ptr<CFXJSE_Value> argThree = GetSimpleValue(pThis, args, 2);
4763     bsEncode = ValueToUTF8String(argThree.get());
4764   }
4765 
4766   if (!pAppProvider->PutRequestURL(
4767           WideString::FromUTF8(bsURL.AsStringView()),
4768           WideString::FromUTF8(bsData.AsStringView()),
4769           WideString::FromUTF8(bsEncode.AsStringView()))) {
4770     pContext->ThrowServerDeniedException();
4771     return;
4772   }
4773 
4774   args.GetReturnValue()->SetString("");
4775 }
4776 
4777 // static
assign_value_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4778 void CFXJSE_FormCalcContext::assign_value_operator(
4779     CFXJSE_Value* pThis,
4780     const ByteStringView& szFuncName,
4781     CFXJSE_Arguments& args) {
4782   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
4783   if (args.GetLength() != 2) {
4784     pContext->ThrowCompilerErrorException();
4785     return;
4786   }
4787 
4788   std::unique_ptr<CFXJSE_Value> lValue = args.GetValue(0);
4789   std::unique_ptr<CFXJSE_Value> rValue = GetSimpleValue(pThis, args, 1);
4790   if (lValue->IsArray()) {
4791     v8::Isolate* pIsolate = pContext->GetScriptRuntime();
4792     auto leftLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
4793     lValue->GetObjectProperty("length", leftLengthValue.get());
4794     int32_t iLeftLength = leftLengthValue->ToInteger();
4795     auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
4796     auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
4797     lValue->GetObjectPropertyByIdx(1, propertyValue.get());
4798     if (propertyValue->IsNull()) {
4799       for (int32_t i = 2; i < iLeftLength; i++) {
4800         lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
4801         if (!SetObjectDefaultValue(jsObjectValue.get(), rValue.get())) {
4802           pContext->ThrowNoDefaultPropertyException(szFuncName);
4803           return;
4804         }
4805       }
4806     } else {
4807       for (int32_t i = 2; i < iLeftLength; i++) {
4808         lValue->GetObjectPropertyByIdx(i, jsObjectValue.get());
4809         jsObjectValue->SetObjectProperty(
4810             propertyValue->ToString().AsStringView(), rValue.get());
4811       }
4812     }
4813   } else if (lValue->IsObject()) {
4814     if (!SetObjectDefaultValue(lValue.get(), rValue.get())) {
4815       pContext->ThrowNoDefaultPropertyException(szFuncName);
4816       return;
4817     }
4818   }
4819   args.GetReturnValue()->Assign(rValue.get());
4820 }
4821 
4822 // static
logical_or_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4823 void CFXJSE_FormCalcContext::logical_or_operator(
4824     CFXJSE_Value* pThis,
4825     const ByteStringView& szFuncName,
4826     CFXJSE_Arguments& args) {
4827   if (args.GetLength() != 2) {
4828     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
4829     return;
4830   }
4831 
4832   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
4833   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
4834   if (argFirst->IsNull() && argSecond->IsNull()) {
4835     args.GetReturnValue()->SetNull();
4836     return;
4837   }
4838 
4839   float first = ValueToFloat(pThis, argFirst.get());
4840   float second = ValueToFloat(pThis, argSecond.get());
4841   args.GetReturnValue()->SetInteger((first || second) ? 1 : 0);
4842 }
4843 
4844 // static
logical_and_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4845 void CFXJSE_FormCalcContext::logical_and_operator(
4846     CFXJSE_Value* pThis,
4847     const ByteStringView& szFuncName,
4848     CFXJSE_Arguments& args) {
4849   if (args.GetLength() != 2) {
4850     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
4851     return;
4852   }
4853 
4854   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
4855   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
4856   if (argFirst->IsNull() && argSecond->IsNull()) {
4857     args.GetReturnValue()->SetNull();
4858     return;
4859   }
4860 
4861   float first = ValueToFloat(pThis, argFirst.get());
4862   float second = ValueToFloat(pThis, argSecond.get());
4863   args.GetReturnValue()->SetInteger((first && second) ? 1 : 0);
4864 }
4865 
4866 // static
equality_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4867 void CFXJSE_FormCalcContext::equality_operator(CFXJSE_Value* pThis,
4868                                                const ByteStringView& szFuncName,
4869                                                CFXJSE_Arguments& args) {
4870   if (args.GetLength() != 2) {
4871     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
4872     return;
4873   }
4874 
4875   if (fm_ref_equal(pThis, args)) {
4876     args.GetReturnValue()->SetInteger(1);
4877     return;
4878   }
4879 
4880   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
4881   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
4882   if (argFirst->IsNull() || argSecond->IsNull()) {
4883     args.GetReturnValue()->SetInteger(
4884         (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
4885     return;
4886   }
4887 
4888   if (argFirst->IsString() && argSecond->IsString()) {
4889     args.GetReturnValue()->SetInteger(argFirst->ToString() ==
4890                                       argSecond->ToString());
4891     return;
4892   }
4893 
4894   double first = ValueToDouble(pThis, argFirst.get());
4895   double second = ValueToDouble(pThis, argSecond.get());
4896   args.GetReturnValue()->SetInteger((first == second) ? 1 : 0);
4897 }
4898 
4899 // static
notequality_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4900 void CFXJSE_FormCalcContext::notequality_operator(
4901     CFXJSE_Value* pThis,
4902     const ByteStringView& szFuncName,
4903     CFXJSE_Arguments& args) {
4904   if (args.GetLength() != 2) {
4905     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
4906     return;
4907   }
4908 
4909   if (fm_ref_equal(pThis, args)) {
4910     args.GetReturnValue()->SetInteger(0);
4911     return;
4912   }
4913 
4914   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
4915   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
4916   if (argFirst->IsNull() || argSecond->IsNull()) {
4917     args.GetReturnValue()->SetInteger(
4918         (argFirst->IsNull() && argSecond->IsNull()) ? 0 : 1);
4919     return;
4920   }
4921 
4922   if (argFirst->IsString() && argSecond->IsString()) {
4923     args.GetReturnValue()->SetInteger(argFirst->ToString() !=
4924                                       argSecond->ToString());
4925     return;
4926   }
4927 
4928   double first = ValueToDouble(pThis, argFirst.get());
4929   double second = ValueToDouble(pThis, argSecond.get());
4930   args.GetReturnValue()->SetInteger(first != second);
4931 }
4932 
4933 // static
fm_ref_equal(CFXJSE_Value * pThis,CFXJSE_Arguments & args)4934 bool CFXJSE_FormCalcContext::fm_ref_equal(CFXJSE_Value* pThis,
4935                                           CFXJSE_Arguments& args) {
4936   std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
4937   std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
4938   if (!argFirst->IsArray() || !argSecond->IsArray())
4939     return false;
4940 
4941   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
4942   auto firstFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
4943   auto secondFlagValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
4944   argFirst->GetObjectPropertyByIdx(0, firstFlagValue.get());
4945   argSecond->GetObjectPropertyByIdx(0, secondFlagValue.get());
4946   if (firstFlagValue->ToInteger() != 3 || secondFlagValue->ToInteger() != 3)
4947     return false;
4948 
4949   auto firstJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
4950   auto secondJSObject = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
4951   argFirst->GetObjectPropertyByIdx(2, firstJSObject.get());
4952   argSecond->GetObjectPropertyByIdx(2, secondJSObject.get());
4953   if (firstJSObject->IsNull() || secondJSObject->IsNull())
4954     return false;
4955 
4956   return (firstJSObject->ToHostObject(nullptr) ==
4957           secondJSObject->ToHostObject(nullptr));
4958 }
4959 
4960 // static
less_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4961 void CFXJSE_FormCalcContext::less_operator(CFXJSE_Value* pThis,
4962                                            const ByteStringView& szFuncName,
4963                                            CFXJSE_Arguments& args) {
4964   if (args.GetLength() != 2) {
4965     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
4966     return;
4967   }
4968 
4969   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
4970   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
4971   if (argFirst->IsNull() || argSecond->IsNull()) {
4972     args.GetReturnValue()->SetInteger(0);
4973     return;
4974   }
4975 
4976   if (argFirst->IsString() && argSecond->IsString()) {
4977     int result =
4978         argFirst->ToString().Compare(argSecond->ToString().AsStringView()) < 0;
4979     args.GetReturnValue()->SetInteger(result);
4980     return;
4981   }
4982 
4983   double first = ValueToDouble(pThis, argFirst.get());
4984   double second = ValueToDouble(pThis, argSecond.get());
4985   args.GetReturnValue()->SetInteger((first < second) ? 1 : 0);
4986 }
4987 
4988 // static
lessequal_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)4989 void CFXJSE_FormCalcContext::lessequal_operator(
4990     CFXJSE_Value* pThis,
4991     const ByteStringView& szFuncName,
4992     CFXJSE_Arguments& args) {
4993   if (args.GetLength() != 2) {
4994     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
4995     return;
4996   }
4997 
4998   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
4999   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
5000   if (argFirst->IsNull() || argSecond->IsNull()) {
5001     args.GetReturnValue()->SetInteger(
5002         (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
5003     return;
5004   }
5005 
5006   if (argFirst->IsString() && argSecond->IsString()) {
5007     int result =
5008         argFirst->ToString().Compare(argSecond->ToString().AsStringView()) <= 0;
5009     args.GetReturnValue()->SetInteger(result);
5010     return;
5011   }
5012 
5013   double first = ValueToDouble(pThis, argFirst.get());
5014   double second = ValueToDouble(pThis, argSecond.get());
5015   args.GetReturnValue()->SetInteger((first <= second) ? 1 : 0);
5016 }
5017 
5018 // static
greater_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5019 void CFXJSE_FormCalcContext::greater_operator(CFXJSE_Value* pThis,
5020                                               const ByteStringView& szFuncName,
5021                                               CFXJSE_Arguments& args) {
5022   if (args.GetLength() != 2) {
5023     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
5024     return;
5025   }
5026 
5027   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
5028   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
5029   if (argFirst->IsNull() || argSecond->IsNull()) {
5030     args.GetReturnValue()->SetInteger(0);
5031     return;
5032   }
5033 
5034   if (argFirst->IsString() && argSecond->IsString()) {
5035     int result =
5036         argFirst->ToString().Compare(argSecond->ToString().AsStringView()) > 0;
5037     args.GetReturnValue()->SetInteger(result);
5038     return;
5039   }
5040 
5041   double first = ValueToDouble(pThis, argFirst.get());
5042   double second = ValueToDouble(pThis, argSecond.get());
5043   args.GetReturnValue()->SetInteger((first > second) ? 1 : 0);
5044 }
5045 
5046 // static
greaterequal_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5047 void CFXJSE_FormCalcContext::greaterequal_operator(
5048     CFXJSE_Value* pThis,
5049     const ByteStringView& szFuncName,
5050     CFXJSE_Arguments& args) {
5051   if (args.GetLength() != 2) {
5052     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
5053     return;
5054   }
5055 
5056   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
5057   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
5058   if (argFirst->IsNull() || argSecond->IsNull()) {
5059     args.GetReturnValue()->SetInteger(
5060         (argFirst->IsNull() && argSecond->IsNull()) ? 1 : 0);
5061     return;
5062   }
5063 
5064   if (argFirst->IsString() && argSecond->IsString()) {
5065     int result =
5066         argFirst->ToString().Compare(argSecond->ToString().AsStringView()) >= 0;
5067     args.GetReturnValue()->SetInteger(result);
5068     return;
5069   }
5070 
5071   double first = ValueToDouble(pThis, argFirst.get());
5072   double second = ValueToDouble(pThis, argSecond.get());
5073   args.GetReturnValue()->SetInteger((first >= second) ? 1 : 0);
5074 }
5075 
5076 // static
plus_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5077 void CFXJSE_FormCalcContext::plus_operator(CFXJSE_Value* pThis,
5078                                            const ByteStringView& szFuncName,
5079                                            CFXJSE_Arguments& args) {
5080   if (args.GetLength() != 2) {
5081     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
5082     return;
5083   }
5084 
5085   std::unique_ptr<CFXJSE_Value> argFirst = args.GetValue(0);
5086   std::unique_ptr<CFXJSE_Value> argSecond = args.GetValue(1);
5087   if (ValueIsNull(pThis, argFirst.get()) &&
5088       ValueIsNull(pThis, argSecond.get())) {
5089     args.GetReturnValue()->SetNull();
5090     return;
5091   }
5092 
5093   double first = ValueToDouble(pThis, argFirst.get());
5094   double second = ValueToDouble(pThis, argSecond.get());
5095   args.GetReturnValue()->SetDouble(first + second);
5096 }
5097 
5098 // static
minus_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5099 void CFXJSE_FormCalcContext::minus_operator(CFXJSE_Value* pThis,
5100                                             const ByteStringView& szFuncName,
5101                                             CFXJSE_Arguments& args) {
5102   if (args.GetLength() != 2) {
5103     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
5104     return;
5105   }
5106 
5107   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
5108   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
5109   if (argFirst->IsNull() && argSecond->IsNull()) {
5110     args.GetReturnValue()->SetNull();
5111     return;
5112   }
5113 
5114   double first = ValueToDouble(pThis, argFirst.get());
5115   double second = ValueToDouble(pThis, argSecond.get());
5116   args.GetReturnValue()->SetDouble(first - second);
5117 }
5118 
5119 // static
multiple_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5120 void CFXJSE_FormCalcContext::multiple_operator(CFXJSE_Value* pThis,
5121                                                const ByteStringView& szFuncName,
5122                                                CFXJSE_Arguments& args) {
5123   if (args.GetLength() != 2) {
5124     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
5125     return;
5126   }
5127 
5128   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
5129   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
5130   if (argFirst->IsNull() && argSecond->IsNull()) {
5131     args.GetReturnValue()->SetNull();
5132     return;
5133   }
5134 
5135   double first = ValueToDouble(pThis, argFirst.get());
5136   double second = ValueToDouble(pThis, argSecond.get());
5137   args.GetReturnValue()->SetDouble(first * second);
5138 }
5139 
5140 // static
divide_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5141 void CFXJSE_FormCalcContext::divide_operator(CFXJSE_Value* pThis,
5142                                              const ByteStringView& szFuncName,
5143                                              CFXJSE_Arguments& args) {
5144   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
5145   if (args.GetLength() != 2) {
5146     pContext->ThrowCompilerErrorException();
5147     return;
5148   }
5149 
5150   std::unique_ptr<CFXJSE_Value> argFirst = GetSimpleValue(pThis, args, 0);
5151   std::unique_ptr<CFXJSE_Value> argSecond = GetSimpleValue(pThis, args, 1);
5152   if (argFirst->IsNull() && argSecond->IsNull()) {
5153     args.GetReturnValue()->SetNull();
5154     return;
5155   }
5156 
5157   double second = ValueToDouble(pThis, argSecond.get());
5158   if (second == 0.0) {
5159     pContext->ThrowDivideByZeroException();
5160     return;
5161   }
5162 
5163   double first = ValueToDouble(pThis, argFirst.get());
5164   args.GetReturnValue()->SetDouble(first / second);
5165 }
5166 
5167 // static
positive_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5168 void CFXJSE_FormCalcContext::positive_operator(CFXJSE_Value* pThis,
5169                                                const ByteStringView& szFuncName,
5170                                                CFXJSE_Arguments& args) {
5171   if (args.GetLength() != 1) {
5172     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
5173     return;
5174   }
5175 
5176   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
5177   if (argOne->IsNull()) {
5178     args.GetReturnValue()->SetNull();
5179     return;
5180   }
5181   args.GetReturnValue()->SetDouble(0.0 + ValueToDouble(pThis, argOne.get()));
5182 }
5183 
5184 // static
negative_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5185 void CFXJSE_FormCalcContext::negative_operator(CFXJSE_Value* pThis,
5186                                                const ByteStringView& szFuncName,
5187                                                CFXJSE_Arguments& args) {
5188   if (args.GetLength() != 1) {
5189     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
5190     return;
5191   }
5192 
5193   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
5194   if (argOne->IsNull()) {
5195     args.GetReturnValue()->SetNull();
5196     return;
5197   }
5198   args.GetReturnValue()->SetDouble(0.0 - ValueToDouble(pThis, argOne.get()));
5199 }
5200 
5201 // static
logical_not_operator(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5202 void CFXJSE_FormCalcContext::logical_not_operator(
5203     CFXJSE_Value* pThis,
5204     const ByteStringView& szFuncName,
5205     CFXJSE_Arguments& args) {
5206   if (args.GetLength() != 1) {
5207     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
5208     return;
5209   }
5210 
5211   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
5212   if (argOne->IsNull()) {
5213     args.GetReturnValue()->SetNull();
5214     return;
5215   }
5216 
5217   double first = ValueToDouble(pThis, argOne.get());
5218   args.GetReturnValue()->SetInteger((first == 0.0) ? 1 : 0);
5219 }
5220 
5221 // static
dot_accessor(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5222 void CFXJSE_FormCalcContext::dot_accessor(CFXJSE_Value* pThis,
5223                                           const ByteStringView& szFuncName,
5224                                           CFXJSE_Arguments& args) {
5225   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
5226   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
5227   int32_t argc = args.GetLength();
5228   if (argc < 4 || argc > 5) {
5229     pContext->ThrowCompilerErrorException();
5230     return;
5231   }
5232 
5233   bool bIsStar = true;
5234   int32_t iIndexValue = 0;
5235   if (argc > 4) {
5236     bIsStar = false;
5237     iIndexValue = ValueToInteger(pThis, args.GetValue(4).get());
5238   }
5239 
5240   ByteString szName = args.GetUTF8String(2);
5241   ByteString szSomExp = GenerateSomExpression(
5242       szName.AsStringView(), args.GetInt32(3), iIndexValue, bIsStar);
5243 
5244   std::unique_ptr<CFXJSE_Value> argAccessor = args.GetValue(0);
5245   if (argAccessor->IsArray()) {
5246     auto pLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5247     argAccessor->GetObjectProperty("length", pLengthValue.get());
5248     int32_t iLength = pLengthValue->ToInteger();
5249     if (iLength < 3) {
5250       pContext->ThrowArgumentMismatchException();
5251       return;
5252     }
5253 
5254     auto hJSObjValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5255     std::vector<std::vector<std::unique_ptr<CFXJSE_Value>>> resolveValues(
5256         iLength - 2);
5257     bool bAttribute = false;
5258     int32_t iCounter = 0;
5259     for (int32_t i = 2; i < iLength; i++) {
5260       argAccessor->GetObjectPropertyByIdx(i, hJSObjValue.get());
5261 
5262       XFA_RESOLVENODE_RS resolveNodeRS;
5263       if (ResolveObjects(pThis, hJSObjValue.get(), szSomExp.AsStringView(),
5264                          &resolveNodeRS, true, szName.IsEmpty())) {
5265         ParseResolveResult(pThis, resolveNodeRS, hJSObjValue.get(),
5266                            &resolveValues[i - 2], &bAttribute);
5267         iCounter += resolveValues[i - 2].size();
5268       }
5269     }
5270     if (iCounter < 1) {
5271       pContext->ThrowPropertyNotInObjectException(
5272           WideString::FromUTF8(szName.AsStringView()),
5273           WideString::FromUTF8(szSomExp.AsStringView()));
5274       return;
5275     }
5276 
5277     std::vector<std::unique_ptr<CFXJSE_Value>> values;
5278     for (int32_t i = 0; i < iCounter + 2; i++)
5279       values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
5280 
5281     values[0]->SetInteger(1);
5282     if (bAttribute)
5283       values[1]->SetString(szName.AsStringView());
5284     else
5285       values[1]->SetNull();
5286 
5287     int32_t iIndex = 2;
5288     for (int32_t i = 0; i < iLength - 2; i++) {
5289       for (size_t j = 0; j < resolveValues[i].size(); j++) {
5290         values[iIndex]->Assign(resolveValues[i][j].get());
5291         iIndex++;
5292       }
5293     }
5294     args.GetReturnValue()->SetArray(values);
5295     return;
5296   }
5297 
5298   XFA_RESOLVENODE_RS resolveNodeRS;
5299   bool iRet = false;
5300   ByteString bsAccessorName = args.GetUTF8String(1);
5301   if (argAccessor->IsObject() ||
5302       (argAccessor->IsNull() && bsAccessorName.IsEmpty())) {
5303     iRet = ResolveObjects(pThis, argAccessor.get(), szSomExp.AsStringView(),
5304                           &resolveNodeRS, true, szName.IsEmpty());
5305   } else if (!argAccessor->IsObject() && !bsAccessorName.IsEmpty() &&
5306              GetObjectForName(pThis, argAccessor.get(),
5307                               bsAccessorName.AsStringView())) {
5308     iRet = ResolveObjects(pThis, argAccessor.get(), szSomExp.AsStringView(),
5309                           &resolveNodeRS, true, szName.IsEmpty());
5310   }
5311   if (!iRet) {
5312     pContext->ThrowPropertyNotInObjectException(
5313         WideString::FromUTF8(szName.AsStringView()),
5314         WideString::FromUTF8(szSomExp.AsStringView()));
5315     return;
5316   }
5317 
5318   std::vector<std::unique_ptr<CFXJSE_Value>> resolveValues;
5319   bool bAttribute = false;
5320   ParseResolveResult(pThis, resolveNodeRS, argAccessor.get(), &resolveValues,
5321                      &bAttribute);
5322 
5323   std::vector<std::unique_ptr<CFXJSE_Value>> values;
5324   for (size_t i = 0; i < resolveValues.size() + 2; i++)
5325     values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
5326 
5327   values[0]->SetInteger(1);
5328   if (bAttribute)
5329     values[1]->SetString(szName.AsStringView());
5330   else
5331     values[1]->SetNull();
5332 
5333   for (size_t i = 0; i < resolveValues.size(); i++)
5334     values[i + 2]->Assign(resolveValues[i].get());
5335 
5336   args.GetReturnValue()->SetArray(values);
5337 }
5338 
5339 // static
dotdot_accessor(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5340 void CFXJSE_FormCalcContext::dotdot_accessor(CFXJSE_Value* pThis,
5341                                              const ByteStringView& szFuncName,
5342                                              CFXJSE_Arguments& args) {
5343   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
5344   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
5345   int32_t argc = args.GetLength();
5346   if (argc < 4 || argc > 5) {
5347     pContext->ThrowCompilerErrorException();
5348     return;
5349   }
5350 
5351   bool bIsStar = true;
5352   int32_t iIndexValue = 0;
5353   if (argc > 4) {
5354     bIsStar = false;
5355     iIndexValue = ValueToInteger(pThis, args.GetValue(4).get());
5356   }
5357 
5358   ByteString szName = args.GetUTF8String(2);
5359   ByteString szSomExp = GenerateSomExpression(
5360       szName.AsStringView(), args.GetInt32(3), iIndexValue, bIsStar);
5361 
5362   std::unique_ptr<CFXJSE_Value> argAccessor = args.GetValue(0);
5363   if (argAccessor->IsArray()) {
5364     auto pLengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5365     argAccessor->GetObjectProperty("length", pLengthValue.get());
5366     int32_t iLength = pLengthValue->ToInteger();
5367     if (iLength < 3) {
5368       pContext->ThrowArgumentMismatchException();
5369       return;
5370     }
5371 
5372     int32_t iCounter = 0;
5373 
5374     std::vector<std::vector<std::unique_ptr<CFXJSE_Value>>> resolveValues(
5375         iLength - 2);
5376     auto hJSObjValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5377     bool bAttribute = false;
5378     for (int32_t i = 2; i < iLength; i++) {
5379       argAccessor->GetObjectPropertyByIdx(i, hJSObjValue.get());
5380       XFA_RESOLVENODE_RS resolveNodeRS;
5381       if (ResolveObjects(pThis, hJSObjValue.get(), szSomExp.AsStringView(),
5382                          &resolveNodeRS, false, false)) {
5383         ParseResolveResult(pThis, resolveNodeRS, hJSObjValue.get(),
5384                            &resolveValues[i - 2], &bAttribute);
5385         iCounter += resolveValues[i - 2].size();
5386       }
5387     }
5388     if (iCounter < 1) {
5389       pContext->ThrowPropertyNotInObjectException(
5390           WideString::FromUTF8(szName.AsStringView()),
5391           WideString::FromUTF8(szSomExp.AsStringView()));
5392       return;
5393     }
5394 
5395     std::vector<std::unique_ptr<CFXJSE_Value>> values;
5396     for (int32_t i = 0; i < iCounter + 2; i++)
5397       values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
5398 
5399     values[0]->SetInteger(1);
5400     if (bAttribute)
5401       values[1]->SetString(szName.AsStringView());
5402     else
5403       values[1]->SetNull();
5404 
5405     int32_t iIndex = 2;
5406     for (int32_t i = 0; i < iLength - 2; i++) {
5407       for (size_t j = 0; j < resolveValues[i].size(); j++) {
5408         values[iIndex]->Assign(resolveValues[i][j].get());
5409         iIndex++;
5410       }
5411     }
5412     args.GetReturnValue()->SetArray(values);
5413     return;
5414   }
5415 
5416   XFA_RESOLVENODE_RS resolveNodeRS;
5417   bool iRet = false;
5418   ByteString bsAccessorName = args.GetUTF8String(1);
5419   if (argAccessor->IsObject() ||
5420       (argAccessor->IsNull() && bsAccessorName.IsEmpty())) {
5421     iRet = ResolveObjects(pThis, argAccessor.get(), szSomExp.AsStringView(),
5422                           &resolveNodeRS, false, false);
5423   } else if (!argAccessor->IsObject() && !bsAccessorName.IsEmpty() &&
5424              GetObjectForName(pThis, argAccessor.get(),
5425                               bsAccessorName.AsStringView())) {
5426     iRet = ResolveObjects(pThis, argAccessor.get(), szSomExp.AsStringView(),
5427                           &resolveNodeRS, false, false);
5428   }
5429   if (!iRet) {
5430     pContext->ThrowPropertyNotInObjectException(
5431         WideString::FromUTF8(szName.AsStringView()),
5432         WideString::FromUTF8(szSomExp.AsStringView()));
5433     return;
5434   }
5435 
5436   std::vector<std::unique_ptr<CFXJSE_Value>> resolveValues;
5437   bool bAttribute = false;
5438   ParseResolveResult(pThis, resolveNodeRS, argAccessor.get(), &resolveValues,
5439                      &bAttribute);
5440 
5441   std::vector<std::unique_ptr<CFXJSE_Value>> values;
5442   for (size_t i = 0; i < resolveValues.size() + 2; i++)
5443     values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
5444 
5445   values[0]->SetInteger(1);
5446   if (bAttribute)
5447     values[1]->SetString(szName.AsStringView());
5448   else
5449     values[1]->SetNull();
5450 
5451   for (size_t i = 0; i < resolveValues.size(); i++)
5452     values[i + 2]->Assign(resolveValues[i].get());
5453 
5454   args.GetReturnValue()->SetArray(values);
5455 }
5456 
5457 // static
eval_translation(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5458 void CFXJSE_FormCalcContext::eval_translation(CFXJSE_Value* pThis,
5459                                               const ByteStringView& szFuncName,
5460                                               CFXJSE_Arguments& args) {
5461   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
5462   if (args.GetLength() != 1) {
5463     pContext->ThrowParamCountMismatchException(L"Eval");
5464     return;
5465   }
5466 
5467   std::unique_ptr<CFXJSE_Value> argOne = GetSimpleValue(pThis, args, 0);
5468   ByteString argString = ValueToUTF8String(argOne.get());
5469   if (argString.IsEmpty()) {
5470     pContext->ThrowArgumentMismatchException();
5471     return;
5472   }
5473 
5474   WideString scriptString = WideString::FromUTF8(argString.AsStringView());
5475   CFX_WideTextBuf wsJavaScriptBuf;
5476   if (!CFXJSE_FormCalcContext::Translate(scriptString.AsStringView(),
5477                                          &wsJavaScriptBuf)) {
5478     pContext->ThrowCompilerErrorException();
5479     return;
5480   }
5481 
5482   args.GetReturnValue()->SetString(
5483       FX_UTF8Encode(wsJavaScriptBuf.AsStringView()).AsStringView());
5484 }
5485 
5486 // static
is_fm_object(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5487 void CFXJSE_FormCalcContext::is_fm_object(CFXJSE_Value* pThis,
5488                                           const ByteStringView& szFuncName,
5489                                           CFXJSE_Arguments& args) {
5490   if (args.GetLength() != 1) {
5491     args.GetReturnValue()->SetBoolean(false);
5492     return;
5493   }
5494 
5495   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
5496   args.GetReturnValue()->SetBoolean(argOne->IsObject());
5497 }
5498 
5499 // static
is_fm_array(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5500 void CFXJSE_FormCalcContext::is_fm_array(CFXJSE_Value* pThis,
5501                                          const ByteStringView& szFuncName,
5502                                          CFXJSE_Arguments& args) {
5503   if (args.GetLength() != 1) {
5504     args.GetReturnValue()->SetBoolean(false);
5505     return;
5506   }
5507 
5508   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
5509   args.GetReturnValue()->SetBoolean(argOne->IsArray());
5510 }
5511 
5512 // static
get_fm_value(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5513 void CFXJSE_FormCalcContext::get_fm_value(CFXJSE_Value* pThis,
5514                                           const ByteStringView& szFuncName,
5515                                           CFXJSE_Arguments& args) {
5516   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
5517   if (args.GetLength() != 1) {
5518     pContext->ThrowCompilerErrorException();
5519     return;
5520   }
5521 
5522   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
5523   if (argOne->IsArray()) {
5524     v8::Isolate* pIsolate = pContext->GetScriptRuntime();
5525     auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5526     auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5527     argOne->GetObjectPropertyByIdx(1, propertyValue.get());
5528     argOne->GetObjectPropertyByIdx(2, jsObjectValue.get());
5529     if (propertyValue->IsNull()) {
5530       GetObjectDefaultValue(jsObjectValue.get(), args.GetReturnValue());
5531       return;
5532     }
5533 
5534     jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
5535                                      args.GetReturnValue());
5536     return;
5537   }
5538 
5539   if (argOne->IsObject()) {
5540     GetObjectDefaultValue(argOne.get(), args.GetReturnValue());
5541     return;
5542   }
5543 
5544   args.GetReturnValue()->Assign(argOne.get());
5545 }
5546 
5547 // static
get_fm_jsobj(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5548 void CFXJSE_FormCalcContext::get_fm_jsobj(CFXJSE_Value* pThis,
5549                                           const ByteStringView& szFuncName,
5550                                           CFXJSE_Arguments& args) {
5551   if (args.GetLength() != 1) {
5552     ToJSContext(pThis, nullptr)->ThrowCompilerErrorException();
5553     return;
5554   }
5555 
5556   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
5557   if (!argOne->IsArray()) {
5558     args.GetReturnValue()->Assign(argOne.get());
5559     return;
5560   }
5561 
5562 #ifndef NDEBUG
5563   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
5564   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
5565   auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5566   argOne->GetObjectProperty("length", lengthValue.get());
5567   ASSERT(lengthValue->ToInteger() >= 3);
5568 #endif
5569 
5570   argOne->GetObjectPropertyByIdx(2, args.GetReturnValue());
5571 }
5572 
5573 // static
fm_var_filter(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5574 void CFXJSE_FormCalcContext::fm_var_filter(CFXJSE_Value* pThis,
5575                                            const ByteStringView& szFuncName,
5576                                            CFXJSE_Arguments& args) {
5577   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
5578   if (args.GetLength() != 1) {
5579     pContext->ThrowCompilerErrorException();
5580     return;
5581   }
5582 
5583   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
5584   std::unique_ptr<CFXJSE_Value> argOne = args.GetValue(0);
5585   if (!argOne->IsArray()) {
5586     std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
5587     args.GetReturnValue()->Assign(simpleValue.get());
5588     return;
5589   }
5590 
5591 #ifndef NDEBUG
5592   auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5593   argOne->GetObjectProperty("length", lengthValue.get());
5594   ASSERT(lengthValue->ToInteger() >= 3);
5595 #endif
5596 
5597   auto flagsValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5598   argOne->GetObjectPropertyByIdx(0, flagsValue.get());
5599   int32_t iFlags = flagsValue->ToInteger();
5600   if (iFlags != 3 && iFlags != 4) {
5601     std::unique_ptr<CFXJSE_Value> simpleValue = GetSimpleValue(pThis, args, 0);
5602     args.GetReturnValue()->Assign(simpleValue.get());
5603     return;
5604   }
5605 
5606   if (iFlags == 4) {
5607     std::vector<std::unique_ptr<CFXJSE_Value>> values;
5608     for (int32_t i = 0; i < 3; i++)
5609       values.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
5610 
5611     values[0]->SetInteger(3);
5612     values[1]->SetNull();
5613     values[2]->SetNull();
5614     args.GetReturnValue()->SetArray(values);
5615     return;
5616   }
5617 
5618   auto objectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5619   argOne->GetObjectPropertyByIdx(2, objectValue.get());
5620   if (objectValue->IsNull()) {
5621     pContext->ThrowCompilerErrorException();
5622     return;
5623   }
5624   args.GetReturnValue()->Assign(argOne.get());
5625 }
5626 
5627 // static
concat_fm_object(CFXJSE_Value * pThis,const ByteStringView & szFuncName,CFXJSE_Arguments & args)5628 void CFXJSE_FormCalcContext::concat_fm_object(CFXJSE_Value* pThis,
5629                                               const ByteStringView& szFuncName,
5630                                               CFXJSE_Arguments& args) {
5631   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
5632   uint32_t iLength = 0;
5633   int32_t argc = args.GetLength();
5634   std::vector<std::unique_ptr<CFXJSE_Value>> argValues;
5635   for (int32_t i = 0; i < argc; i++) {
5636     argValues.push_back(args.GetValue(i));
5637     if (argValues[i]->IsArray()) {
5638       auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5639       argValues[i]->GetObjectProperty("length", lengthValue.get());
5640       int32_t length = lengthValue->ToInteger();
5641       iLength = iLength + ((length > 2) ? (length - 2) : 0);
5642     }
5643     iLength += 1;
5644   }
5645 
5646   std::vector<std::unique_ptr<CFXJSE_Value>> returnValues;
5647   for (int32_t i = 0; i < (int32_t)iLength; i++)
5648     returnValues.push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
5649 
5650   int32_t index = 0;
5651   for (int32_t i = 0; i < argc; i++) {
5652     if (argValues[i]->IsArray()) {
5653       auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5654       argValues[i]->GetObjectProperty("length", lengthValue.get());
5655 
5656       int32_t length = lengthValue->ToInteger();
5657       for (int32_t j = 2; j < length; j++) {
5658         argValues[i]->GetObjectPropertyByIdx(j, returnValues[index].get());
5659         index++;
5660       }
5661     }
5662     returnValues[index]->Assign(argValues[i].get());
5663     index++;
5664   }
5665   args.GetReturnValue()->SetArray(returnValues);
5666 }
5667 
5668 // static
GetSimpleValue(CFXJSE_Value * pThis,CFXJSE_Arguments & args,uint32_t index)5669 std::unique_ptr<CFXJSE_Value> CFXJSE_FormCalcContext::GetSimpleValue(
5670     CFXJSE_Value* pThis,
5671     CFXJSE_Arguments& args,
5672     uint32_t index) {
5673   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
5674   ASSERT(index < (uint32_t)args.GetLength());
5675 
5676   std::unique_ptr<CFXJSE_Value> argIndex = args.GetValue(index);
5677   if (!argIndex->IsArray() && !argIndex->IsObject())
5678     return argIndex;
5679 
5680   if (argIndex->IsArray()) {
5681     auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5682     argIndex->GetObjectProperty("length", lengthValue.get());
5683     int32_t iLength = lengthValue->ToInteger();
5684     auto simpleValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5685     if (iLength < 3) {
5686       simpleValue.get()->SetUndefined();
5687       return simpleValue;
5688     }
5689 
5690     auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5691     auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5692     argIndex->GetObjectPropertyByIdx(1, propertyValue.get());
5693     argIndex->GetObjectPropertyByIdx(2, jsObjectValue.get());
5694     if (propertyValue->IsNull()) {
5695       GetObjectDefaultValue(jsObjectValue.get(), simpleValue.get());
5696       return simpleValue;
5697     }
5698 
5699     jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
5700                                      simpleValue.get());
5701     return simpleValue;
5702   }
5703 
5704   auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5705   GetObjectDefaultValue(argIndex.get(), defaultValue.get());
5706   return defaultValue;
5707 }
5708 
5709 // static
ValueIsNull(CFXJSE_Value * pThis,CFXJSE_Value * arg)5710 bool CFXJSE_FormCalcContext::ValueIsNull(CFXJSE_Value* pThis,
5711                                          CFXJSE_Value* arg) {
5712   if (!arg || arg->IsNull())
5713     return true;
5714 
5715   if (!arg->IsArray() && !arg->IsObject())
5716     return false;
5717 
5718   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
5719   if (arg->IsArray()) {
5720     int32_t iLength = hvalue_get_array_length(pThis, arg);
5721     if (iLength < 3)
5722       return true;
5723 
5724     auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5725     auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5726     arg->GetObjectPropertyByIdx(1, propertyValue.get());
5727     arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
5728     if (propertyValue->IsNull()) {
5729       auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5730       GetObjectDefaultValue(jsObjectValue.get(), defaultValue.get());
5731       return defaultValue->IsNull();
5732     }
5733 
5734     auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5735     jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
5736                                      newPropertyValue.get());
5737     return newPropertyValue->IsNull();
5738   }
5739 
5740   auto defaultValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5741   GetObjectDefaultValue(arg, defaultValue.get());
5742   return defaultValue->IsNull();
5743 }
5744 
5745 // static
hvalue_get_array_length(CFXJSE_Value * pThis,CFXJSE_Value * arg)5746 int32_t CFXJSE_FormCalcContext::hvalue_get_array_length(CFXJSE_Value* pThis,
5747                                                         CFXJSE_Value* arg) {
5748   if (!arg || !arg->IsArray())
5749     return 0;
5750 
5751   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
5752   auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5753   arg->GetObjectProperty("length", lengthValue.get());
5754   return lengthValue->ToInteger();
5755 }
5756 
5757 // static
simpleValueCompare(CFXJSE_Value * pThis,CFXJSE_Value * firstValue,CFXJSE_Value * secondValue)5758 bool CFXJSE_FormCalcContext::simpleValueCompare(CFXJSE_Value* pThis,
5759                                                 CFXJSE_Value* firstValue,
5760                                                 CFXJSE_Value* secondValue) {
5761   if (!firstValue)
5762     return false;
5763 
5764   if (firstValue->IsString()) {
5765     ByteString firstString = ValueToUTF8String(firstValue);
5766     ByteString secondString = ValueToUTF8String(secondValue);
5767     return firstString == secondString;
5768   }
5769   if (firstValue->IsNumber()) {
5770     float first = ValueToFloat(pThis, firstValue);
5771     float second = ValueToFloat(pThis, secondValue);
5772     return first == second;
5773   }
5774   if (firstValue->IsBoolean())
5775     return firstValue->ToBoolean() == secondValue->ToBoolean();
5776 
5777   return firstValue->IsNull() && secondValue && secondValue->IsNull();
5778 }
5779 
5780 // static
unfoldArgs(CFXJSE_Value * pThis,CFXJSE_Arguments & args,std::vector<std::unique_ptr<CFXJSE_Value>> * resultValues,int32_t iStart)5781 void CFXJSE_FormCalcContext::unfoldArgs(
5782     CFXJSE_Value* pThis,
5783     CFXJSE_Arguments& args,
5784     std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
5785     int32_t iStart) {
5786   resultValues->clear();
5787 
5788   int32_t iCount = 0;
5789   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
5790   int32_t argc = args.GetLength();
5791   std::vector<std::unique_ptr<CFXJSE_Value>> argsValue;
5792   for (int32_t i = 0; i < argc - iStart; i++) {
5793     argsValue.push_back(args.GetValue(i + iStart));
5794     if (argsValue[i]->IsArray()) {
5795       auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5796       argsValue[i]->GetObjectProperty("length", lengthValue.get());
5797       int32_t iLength = lengthValue->ToInteger();
5798       iCount += ((iLength > 2) ? (iLength - 2) : 0);
5799     } else {
5800       iCount += 1;
5801     }
5802   }
5803 
5804   for (int32_t i = 0; i < iCount; i++)
5805     resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
5806 
5807   int32_t index = 0;
5808   for (int32_t i = 0; i < argc - iStart; i++) {
5809     if (argsValue[i]->IsArray()) {
5810       auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5811       argsValue[i]->GetObjectProperty("length", lengthValue.get());
5812       int32_t iLength = lengthValue->ToInteger();
5813       if (iLength < 3)
5814         continue;
5815 
5816       auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5817       auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
5818       argsValue[i]->GetObjectPropertyByIdx(1, propertyValue.get());
5819       if (propertyValue->IsNull()) {
5820         for (int32_t j = 2; j < iLength; j++) {
5821           argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
5822           GetObjectDefaultValue(jsObjectValue.get(),
5823                                 (*resultValues)[index].get());
5824           index++;
5825         }
5826       } else {
5827         for (int32_t j = 2; j < iLength; j++) {
5828           argsValue[i]->GetObjectPropertyByIdx(j, jsObjectValue.get());
5829           jsObjectValue->GetObjectProperty(
5830               propertyValue->ToString().AsStringView(),
5831               (*resultValues)[index].get());
5832           index++;
5833         }
5834       }
5835     } else if (argsValue[i]->IsObject()) {
5836       GetObjectDefaultValue(argsValue[i].get(), (*resultValues)[index].get());
5837       index++;
5838     } else {
5839       (*resultValues)[index]->Assign(argsValue[i].get());
5840       index++;
5841     }
5842   }
5843 }
5844 
5845 // static
GetObjectDefaultValue(CFXJSE_Value * pValue,CFXJSE_Value * pDefaultValue)5846 void CFXJSE_FormCalcContext::GetObjectDefaultValue(
5847     CFXJSE_Value* pValue,
5848     CFXJSE_Value* pDefaultValue) {
5849   CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue, nullptr));
5850   if (!pNode) {
5851     pDefaultValue->SetNull();
5852     return;
5853   }
5854   pNode->JSObject()->Script_Som_DefaultValue(pDefaultValue, false,
5855                                              XFA_Attribute::Unknown);
5856 }
5857 
5858 // static
SetObjectDefaultValue(CFXJSE_Value * pValue,CFXJSE_Value * hNewValue)5859 bool CFXJSE_FormCalcContext::SetObjectDefaultValue(CFXJSE_Value* pValue,
5860                                                    CFXJSE_Value* hNewValue) {
5861   CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pValue, nullptr));
5862   if (!pNode)
5863     return false;
5864 
5865   pNode->JSObject()->Script_Som_DefaultValue(hNewValue, true,
5866                                              XFA_Attribute::Unknown);
5867   return true;
5868 }
5869 
5870 // static
GenerateSomExpression(const ByteStringView & szName,int32_t iIndexFlags,int32_t iIndexValue,bool bIsStar)5871 ByteString CFXJSE_FormCalcContext::GenerateSomExpression(
5872     const ByteStringView& szName,
5873     int32_t iIndexFlags,
5874     int32_t iIndexValue,
5875     bool bIsStar) {
5876   if (bIsStar)
5877     return ByteString(szName, "[*]");
5878 
5879   if (iIndexFlags == 0)
5880     return ByteString(szName);
5881 
5882   if (iIndexFlags == 1 || iIndexValue == 0) {
5883     return ByteString(szName, "[") + ByteString::FormatInteger(iIndexValue) +
5884            "]";
5885   }
5886   ByteString szSomExp;
5887   if (iIndexFlags == 2) {
5888     szSomExp = (iIndexValue < 0) ? (szName + "[-") : (szName + "[+");
5889     iIndexValue = (iIndexValue < 0) ? (0 - iIndexValue) : iIndexValue;
5890     szSomExp += ByteString::FormatInteger(iIndexValue);
5891     szSomExp += "]";
5892   } else {
5893     szSomExp = (iIndexValue < 0) ? (szName + "[") : (szName + "[-");
5894     iIndexValue = (iIndexValue < 0) ? (0 - iIndexValue) : iIndexValue;
5895     szSomExp += ByteString::FormatInteger(iIndexValue);
5896     szSomExp += "]";
5897   }
5898   return szSomExp;
5899 }
5900 
5901 // static
GetObjectForName(CFXJSE_Value * pThis,CFXJSE_Value * accessorValue,const ByteStringView & szAccessorName)5902 bool CFXJSE_FormCalcContext::GetObjectForName(
5903     CFXJSE_Value* pThis,
5904     CFXJSE_Value* accessorValue,
5905     const ByteStringView& szAccessorName) {
5906   CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
5907   if (!pDoc)
5908     return false;
5909 
5910   CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
5911   XFA_RESOLVENODE_RS resolveNodeRS;
5912   uint32_t dwFlags = XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Properties |
5913                      XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
5914   bool iRet = pScriptContext->ResolveObjects(
5915       pScriptContext->GetThisObject(),
5916       WideString::FromUTF8(szAccessorName).AsStringView(), &resolveNodeRS,
5917       dwFlags, nullptr);
5918   if (iRet && resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
5919     accessorValue->Assign(
5920         pScriptContext->GetJSValueFromMap(resolveNodeRS.objects.front()));
5921     return true;
5922   }
5923   return false;
5924 }
5925 
5926 // static
ResolveObjects(CFXJSE_Value * pThis,CFXJSE_Value * pRefValue,const ByteStringView & bsSomExp,XFA_RESOLVENODE_RS * resolveNodeRS,bool bdotAccessor,bool bHasNoResolveName)5927 bool CFXJSE_FormCalcContext::ResolveObjects(CFXJSE_Value* pThis,
5928                                             CFXJSE_Value* pRefValue,
5929                                             const ByteStringView& bsSomExp,
5930                                             XFA_RESOLVENODE_RS* resolveNodeRS,
5931                                             bool bdotAccessor,
5932                                             bool bHasNoResolveName) {
5933   CXFA_Document* pDoc = ToJSContext(pThis, nullptr)->GetDocument();
5934   if (!pDoc)
5935     return false;
5936 
5937   WideString wsSomExpression = WideString::FromUTF8(bsSomExp);
5938   CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext();
5939   CXFA_Object* pNode = nullptr;
5940   uint32_t dFlags = 0UL;
5941   if (bdotAccessor) {
5942     if (pRefValue && pRefValue->IsNull()) {
5943       pNode = pScriptContext->GetThisObject();
5944       dFlags = XFA_RESOLVENODE_Siblings | XFA_RESOLVENODE_Parent;
5945     } else {
5946       pNode = CFXJSE_Engine::ToObject(pRefValue, nullptr);
5947       ASSERT(pNode);
5948       if (bHasNoResolveName) {
5949         WideString wsName;
5950         if (CXFA_Node* pXFANode = pNode->AsNode()) {
5951           Optional<WideString> ret =
5952               pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false);
5953           if (ret)
5954             wsName = *ret;
5955         }
5956         if (wsName.IsEmpty())
5957           wsName = L"#" + pNode->GetClassName();
5958 
5959         wsSomExpression = wsName + wsSomExpression;
5960         dFlags = XFA_RESOLVENODE_Siblings;
5961       } else {
5962         dFlags = (bsSomExp == "*")
5963                      ? (XFA_RESOLVENODE_Children)
5964                      : (XFA_RESOLVENODE_Children | XFA_RESOLVENODE_Attributes |
5965                         XFA_RESOLVENODE_Properties);
5966       }
5967     }
5968   } else {
5969     pNode = CFXJSE_Engine::ToObject(pRefValue, nullptr);
5970     dFlags = XFA_RESOLVENODE_AnyChild;
5971   }
5972   return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(),
5973                                         resolveNodeRS, dFlags, nullptr);
5974 }
5975 
5976 // static
ParseResolveResult(CFXJSE_Value * pThis,const XFA_RESOLVENODE_RS & resolveNodeRS,CFXJSE_Value * pParentValue,std::vector<std::unique_ptr<CFXJSE_Value>> * resultValues,bool * bAttribute)5977 void CFXJSE_FormCalcContext::ParseResolveResult(
5978     CFXJSE_Value* pThis,
5979     const XFA_RESOLVENODE_RS& resolveNodeRS,
5980     CFXJSE_Value* pParentValue,
5981     std::vector<std::unique_ptr<CFXJSE_Value>>* resultValues,
5982     bool* bAttribute) {
5983   ASSERT(bAttribute);
5984 
5985   resultValues->clear();
5986 
5987   CFXJSE_FormCalcContext* pContext = ToJSContext(pThis, nullptr);
5988   v8::Isolate* pIsolate = pContext->GetScriptRuntime();
5989 
5990   if (resolveNodeRS.dwFlags == XFA_ResolveNode_RSType_Nodes) {
5991     *bAttribute = false;
5992     CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext();
5993     for (CXFA_Object* pObject : resolveNodeRS.objects) {
5994       resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
5995       resultValues->back()->Assign(pScriptContext->GetJSValueFromMap(pObject));
5996     }
5997     return;
5998   }
5999 
6000   *bAttribute = true;
6001   if (resolveNodeRS.pScriptAttribute &&
6002       resolveNodeRS.pScriptAttribute->eValueType == XFA_ScriptType::Object) {
6003     for (CXFA_Object* pObject : resolveNodeRS.objects) {
6004       auto pValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6005       CJX_Object* jsObject = pObject->JSObject();
6006       (jsObject->*(resolveNodeRS.pScriptAttribute->callback))(
6007           pValue.get(), false, resolveNodeRS.pScriptAttribute->attribute);
6008 
6009       resultValues->push_back(std::move(pValue));
6010       *bAttribute = false;
6011     }
6012   }
6013   if (!*bAttribute)
6014     return;
6015   if (!pParentValue || !pParentValue->IsObject())
6016     return;
6017 
6018   resultValues->push_back(pdfium::MakeUnique<CFXJSE_Value>(pIsolate));
6019   resultValues->back()->Assign(pParentValue);
6020 }
6021 
6022 // static
ValueToInteger(CFXJSE_Value * pThis,CFXJSE_Value * pValue)6023 int32_t CFXJSE_FormCalcContext::ValueToInteger(CFXJSE_Value* pThis,
6024                                                CFXJSE_Value* pValue) {
6025   if (!pValue)
6026     return 0;
6027 
6028   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
6029   if (pValue->IsArray()) {
6030     auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6031     auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6032     auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6033     pValue->GetObjectPropertyByIdx(1, propertyValue.get());
6034     pValue->GetObjectPropertyByIdx(2, jsObjectValue.get());
6035     if (propertyValue->IsNull()) {
6036       GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
6037       return ValueToInteger(pThis, newPropertyValue.get());
6038     }
6039 
6040     jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
6041                                      newPropertyValue.get());
6042     return ValueToInteger(pThis, newPropertyValue.get());
6043   }
6044   if (pValue->IsObject()) {
6045     auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6046     GetObjectDefaultValue(pValue, newPropertyValue.get());
6047     return ValueToInteger(pThis, newPropertyValue.get());
6048   }
6049   if (pValue->IsString())
6050     return FXSYS_atoi(pValue->ToString().c_str());
6051   return pValue->ToInteger();
6052 }
6053 
6054 // static
ValueToFloat(CFXJSE_Value * pThis,CFXJSE_Value * arg)6055 float CFXJSE_FormCalcContext::ValueToFloat(CFXJSE_Value* pThis,
6056                                            CFXJSE_Value* arg) {
6057   if (!arg)
6058     return 0.0f;
6059 
6060   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
6061   if (arg->IsArray()) {
6062     auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6063     auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6064     auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6065     arg->GetObjectPropertyByIdx(1, propertyValue.get());
6066     arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
6067     if (propertyValue->IsNull()) {
6068       GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
6069       return ValueToFloat(pThis, newPropertyValue.get());
6070     }
6071     jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
6072                                      newPropertyValue.get());
6073     return ValueToFloat(pThis, newPropertyValue.get());
6074   }
6075   if (arg->IsObject()) {
6076     auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6077     GetObjectDefaultValue(arg, newPropertyValue.get());
6078     return ValueToFloat(pThis, newPropertyValue.get());
6079   }
6080   if (arg->IsString()) {
6081     return static_cast<float>(
6082         ByteStringToDouble(arg->ToString().AsStringView()));
6083   }
6084   if (arg->IsUndefined())
6085     return 0;
6086 
6087   return arg->ToFloat();
6088 }
6089 
6090 // static
ValueToDouble(CFXJSE_Value * pThis,CFXJSE_Value * arg)6091 double CFXJSE_FormCalcContext::ValueToDouble(CFXJSE_Value* pThis,
6092                                              CFXJSE_Value* arg) {
6093   if (!arg)
6094     return 0;
6095 
6096   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
6097   if (arg->IsArray()) {
6098     auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6099     auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6100     auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6101     arg->GetObjectPropertyByIdx(1, propertyValue.get());
6102     arg->GetObjectPropertyByIdx(2, jsObjectValue.get());
6103     if (propertyValue->IsNull()) {
6104       GetObjectDefaultValue(jsObjectValue.get(), newPropertyValue.get());
6105       return ValueToDouble(pThis, newPropertyValue.get());
6106     }
6107     jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
6108                                      newPropertyValue.get());
6109     return ValueToDouble(pThis, newPropertyValue.get());
6110   }
6111   if (arg->IsObject()) {
6112     auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6113     GetObjectDefaultValue(arg, newPropertyValue.get());
6114     return ValueToDouble(pThis, newPropertyValue.get());
6115   }
6116   if (arg->IsString())
6117     return ByteStringToDouble(arg->ToString().AsStringView());
6118   if (arg->IsUndefined())
6119     return 0;
6120   return arg->ToDouble();
6121 }
6122 
6123 // static.
ExtractDouble(CFXJSE_Value * pThis,CFXJSE_Value * src,bool * ret)6124 double CFXJSE_FormCalcContext::ExtractDouble(CFXJSE_Value* pThis,
6125                                              CFXJSE_Value* src,
6126                                              bool* ret) {
6127   ASSERT(ret);
6128   *ret = true;
6129 
6130   if (!src)
6131     return 0;
6132 
6133   if (!src->IsArray())
6134     return ValueToDouble(pThis, src);
6135 
6136   v8::Isolate* pIsolate = ToJSContext(pThis, nullptr)->GetScriptRuntime();
6137   auto lengthValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6138   src->GetObjectProperty("length", lengthValue.get());
6139   int32_t iLength = lengthValue->ToInteger();
6140   if (iLength <= 2) {
6141     *ret = false;
6142     return 0.0;
6143   }
6144 
6145   auto propertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6146   auto jsObjectValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6147   src->GetObjectPropertyByIdx(1, propertyValue.get());
6148   src->GetObjectPropertyByIdx(2, jsObjectValue.get());
6149   if (propertyValue->IsNull())
6150     return ValueToDouble(pThis, jsObjectValue.get());
6151 
6152   auto newPropertyValue = pdfium::MakeUnique<CFXJSE_Value>(pIsolate);
6153   jsObjectValue->GetObjectProperty(propertyValue->ToString().AsStringView(),
6154                                    newPropertyValue.get());
6155   return ValueToDouble(pThis, newPropertyValue.get());
6156 }
6157 
6158 // static
ValueToUTF8String(CFXJSE_Value * arg)6159 ByteString CFXJSE_FormCalcContext::ValueToUTF8String(CFXJSE_Value* arg) {
6160   if (!arg || arg->IsNull() || arg->IsUndefined())
6161     return ByteString();
6162   if (arg->IsBoolean())
6163     return arg->ToBoolean() ? "1" : "0";
6164   return arg->ToString();
6165 }
6166 
6167 // static.
Translate(const WideStringView & wsFormcalc,CFX_WideTextBuf * wsJavascript)6168 bool CFXJSE_FormCalcContext::Translate(const WideStringView& wsFormcalc,
6169                                        CFX_WideTextBuf* wsJavascript) {
6170   if (wsFormcalc.IsEmpty()) {
6171     wsJavascript->Clear();
6172     return true;
6173   }
6174 
6175   CXFA_FMParser parser(wsFormcalc);
6176   std::unique_ptr<CXFA_FMFunctionDefinition> func = parser.Parse();
6177   if (!func || parser.HasError())
6178     return false;
6179 
6180   CXFA_FMToJavaScriptDepth::Reset();
6181   if (!func->ToJavaScript(*wsJavascript))
6182     return false;
6183 
6184   wsJavascript->AppendChar(0);
6185 
6186   return !CXFA_IsTooBig(*wsJavascript);
6187 }
6188 
CFXJSE_FormCalcContext(v8::Isolate * pScriptIsolate,CFXJSE_Context * pScriptContext,CXFA_Document * pDoc)6189 CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pScriptIsolate,
6190                                                CFXJSE_Context* pScriptContext,
6191                                                CXFA_Document* pDoc)
6192     : CFXJSE_HostObject(kFM2JS),
6193       m_pIsolate(pScriptIsolate),
6194       m_pFMClass(CFXJSE_Class::Create(pScriptContext,
6195                                       &formcalc_fm2js_descriptor,
6196                                       false)),
6197       m_pValue(pdfium::MakeUnique<CFXJSE_Value>(pScriptIsolate)),
6198       m_pDocument(pDoc) {
6199   m_pValue.get()->SetObject(this, m_pFMClass);
6200 }
6201 
~CFXJSE_FormCalcContext()6202 CFXJSE_FormCalcContext::~CFXJSE_FormCalcContext() {}
6203 
GlobalPropertyGetter(CFXJSE_Value * pValue)6204 void CFXJSE_FormCalcContext::GlobalPropertyGetter(CFXJSE_Value* pValue) {
6205   pValue->Assign(m_pValue.get());
6206 }
6207 
ThrowNoDefaultPropertyException(const ByteStringView & name) const6208 void CFXJSE_FormCalcContext::ThrowNoDefaultPropertyException(
6209     const ByteStringView& name) const {
6210   // TODO(tsepez): check usage of c_str() below.
6211   ThrowException(L"%.16S doesn't have a default property.",
6212                  name.unterminated_c_str());
6213 }
6214 
ThrowCompilerErrorException() const6215 void CFXJSE_FormCalcContext::ThrowCompilerErrorException() const {
6216   ThrowException(L"Compiler error.");
6217 }
6218 
ThrowDivideByZeroException() const6219 void CFXJSE_FormCalcContext::ThrowDivideByZeroException() const {
6220   ThrowException(L"Divide by zero.");
6221 }
6222 
ThrowServerDeniedException() const6223 void CFXJSE_FormCalcContext::ThrowServerDeniedException() const {
6224   ThrowException(L"Server does not permit operation.");
6225 }
6226 
ThrowPropertyNotInObjectException(const WideString & name,const WideString & exp) const6227 void CFXJSE_FormCalcContext::ThrowPropertyNotInObjectException(
6228     const WideString& name,
6229     const WideString& exp) const {
6230   ThrowException(
6231       L"An attempt was made to reference property '%.16s' of a non-object "
6232       L"in SOM expression %.16s.",
6233       name.c_str(), exp.c_str());
6234 }
6235 
ThrowParamCountMismatchException(const WideString & method) const6236 void CFXJSE_FormCalcContext::ThrowParamCountMismatchException(
6237     const WideString& method) const {
6238   ThrowException(L"Incorrect number of parameters calling method '%.16s'.",
6239                  method.c_str());
6240 }
6241 
ThrowArgumentMismatchException() const6242 void CFXJSE_FormCalcContext::ThrowArgumentMismatchException() const {
6243   ThrowException(L"Argument mismatch in property or function argument.");
6244 }
6245 
ThrowException(const wchar_t * str,...) const6246 void CFXJSE_FormCalcContext::ThrowException(const wchar_t* str, ...) const {
6247   va_list arg_ptr;
6248   va_start(arg_ptr, str);
6249   WideString wsMessage = WideString::FormatV(str, arg_ptr);
6250   va_end(arg_ptr);
6251 
6252   ASSERT(!wsMessage.IsEmpty());
6253   FXJSE_ThrowMessage(wsMessage.UTF8Encode().AsStringView());
6254 }
6255