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