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