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