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