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