// Copyright 2014 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "fxjs/xfa/cfxjse_formcalc_context.h" #include #include #include #include #include #include #include #include #include #include #include #include "core/fxcrt/cfx_datetime.h" #include "core/fxcrt/check_op.h" #include "core/fxcrt/code_point_view.h" #include "core/fxcrt/compiler_specific.h" #include "core/fxcrt/containers/contains.h" #include "core/fxcrt/data_vector.h" #include "core/fxcrt/fx_extension.h" #include "core/fxcrt/fx_random.h" #include "core/fxcrt/fx_safe_types.h" #include "core/fxcrt/numerics/safe_conversions.h" #include "core/fxcrt/span_util.h" #include "core/fxcrt/widetext_buffer.h" #include "fxjs/fxv8.h" #include "fxjs/xfa/cfxjse_class.h" #include "fxjs/xfa/cfxjse_context.h" #include "fxjs/xfa/cfxjse_engine.h" #include "fxjs/xfa/cfxjse_value.h" #include "fxjs/xfa/cjx_object.h" #include "v8/include/v8-container.h" #include "v8/include/v8-function-callback.h" #include "v8/include/v8-local-handle.h" #include "v8/include/v8-object.h" #include "v8/include/v8-primitive.h" #include "xfa/fgas/crt/cfgas_decimal.h" #include "xfa/fxfa/cxfa_ffnotify.h" #include "xfa/fxfa/formcalc/cxfa_fmparser.h" #include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h" #include "xfa/fxfa/parser/cxfa_document.h" #include "xfa/fxfa/parser/cxfa_localevalue.h" #include "xfa/fxfa/parser/cxfa_node.h" #include "xfa/fxfa/parser/cxfa_thisproxy.h" #include "xfa/fxfa/parser/cxfa_timezoneprovider.h" #include "xfa/fxfa/parser/gced_locale_iface.h" #include "xfa/fxfa/parser/xfa_utils.h" using pdfium::fxjse::kClassTag; using pdfium::fxjse::kFuncTag; namespace { // Maximum number of characters Acrobat can fit in a text box. constexpr int kMaxCharCount = 15654908; const double kFinancialPrecision = 0.00000001; constexpr std::array kStrCode = { {L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'a', L'b', L'c', L'd', L'e', L'f'}}; struct XFA_FMHtmlReserveCode { uint16_t m_uCode; // Inline string data reduces size for small strings. const char m_htmlReserve[10]; }; // Sorted by |m_htmlReserve|. const XFA_FMHtmlReserveCode kReservesForDecode[] = { {198, "AElig"}, {193, "Aacute"}, {194, "Acirc"}, {192, "Agrave"}, {913, "Alpha"}, {197, "Aring"}, {195, "Atilde"}, {196, "Auml"}, {914, "Beta"}, {199, "Ccedil"}, {935, "Chi"}, {8225, "Dagger"}, {916, "Delta"}, {208, "ETH"}, {201, "Eacute"}, {202, "Ecirc"}, {200, "Egrave"}, {917, "Epsilon"}, {919, "Eta"}, {203, "Euml"}, {915, "Gamma"}, {922, "Kappa"}, {923, "Lambda"}, {924, "Mu"}, {209, "Ntilde"}, {925, "Nu"}, {338, "OElig"}, {211, "Oacute"}, {212, "Ocirc"}, {210, "Ograve"}, {937, "Omega"}, {927, "Omicron"}, {216, "Oslash"}, {213, "Otilde"}, {214, "Ouml"}, {934, "Phi"}, {928, "Pi"}, {936, "Psi"}, {929, "Rho"}, {352, "Scaron"}, {931, "Sigma"}, {222, "THORN"}, {932, "Tau"}, {920, "Theta"}, {218, "Uacute"}, {219, "Ucirc"}, {217, "Ugrave"}, {933, "Upsilon"}, {220, "Uuml"}, {926, "Xi"}, {221, "Yacute"}, {376, "Yuml"}, {918, "Zeta"}, {225, "aacute"}, {226, "acirc"}, {180, "acute"}, {230, "aelig"}, {224, "agrave"}, {8501, "alefsym"}, {945, "alpha"}, {38, "amp"}, {8743, "and"}, {8736, "ang"}, {39, "apos"}, {229, "aring"}, {8776, "asymp"}, {227, "atilde"}, {228, "auml"}, {8222, "bdquo"}, {946, "beta"}, {166, "brvbar"}, {8226, "bull"}, {8745, "cap"}, {231, "ccedil"}, {184, "cedil"}, {162, "cent"}, {967, "chi"}, {710, "circ"}, {9827, "clubs"}, {8773, "cong"}, {169, "copy"}, {8629, "crarr"}, {8746, "cup"}, {164, "current"}, {8659, "dArr"}, {8224, "dagger"}, {8595, "darr"}, {176, "deg"}, {948, "delta"}, {9830, "diams"}, {247, "divide"}, {233, "eacute"}, {234, "ecirc"}, {232, "egrave"}, {8709, "empty"}, {8195, "emsp"}, {8194, "ensp"}, {949, "epsilon"}, {8801, "equiv"}, {951, "eta"}, {240, "eth"}, {235, "euml"}, {8364, "euro"}, {8707, "exist"}, {402, "fnof"}, {8704, "forall"}, {189, "frac12"}, {188, "frac14"}, {190, "frac34"}, {8260, "frasl"}, {947, "gamma"}, {8805, "ge"}, {62, "gt"}, {8660, "hArr"}, {8596, "harr"}, {9829, "hearts"}, {8230, "hellip"}, {237, "iacute"}, {238, "icirc"}, {161, "iexcl"}, {236, "igrave"}, {8465, "image"}, {8734, "infin"}, {8747, "int"}, {953, "iota"}, {191, "iquest"}, {8712, "isin"}, {239, "iuml"}, {954, "kappa"}, {8656, "lArr"}, {205, "lacute"}, {955, "lambda"}, {9001, "lang"}, {171, "laquo"}, {8592, "larr"}, {8968, "lceil"}, {206, "lcirc"}, {8220, "ldquo"}, {8804, "le"}, {8970, "lfloor"}, {204, "lgrave"}, {921, "lota"}, {8727, "lowast"}, {9674, "loz"}, {8206, "lrm"}, {8249, "lsaquo"}, {8216, "lsquo"}, {60, "lt"}, {207, "luml"}, {175, "macr"}, {8212, "mdash"}, {181, "micro"}, {183, "middot"}, {8722, "minus"}, {956, "mu"}, {8711, "nabla"}, {160, "nbsp"}, {8211, "ndash"}, {8800, "ne"}, {8715, "ni"}, {172, "not"}, {8713, "notin"}, {8836, "nsub"}, {241, "ntilde"}, {957, "nu"}, {243, "oacute"}, {244, "ocirc"}, {339, "oelig"}, {242, "ograve"}, {8254, "oline"}, {969, "omega"}, {959, "omicron"}, {8853, "oplus"}, {8744, "or"}, {170, "ordf"}, {186, "ordm"}, {248, "oslash"}, {245, "otilde"}, {8855, "otimes"}, {246, "ouml"}, {182, "para"}, {8706, "part"}, {8240, "permil"}, {8869, "perp"}, {966, "phi"}, {960, "pi"}, {982, "piv"}, {177, "plusmn"}, {8242, "prime"}, {8719, "prod"}, {8733, "prop"}, {968, "psi"}, {163, "pund"}, {34, "quot"}, {8658, "rArr"}, {8730, "radic"}, {9002, "rang"}, {187, "raquo"}, {8594, "rarr"}, {8969, "rceil"}, {8476, "real"}, {174, "reg"}, {8971, "rfloor"}, {961, "rho"}, {8207, "rlm"}, {8250, "rsaquo"}, {8217, "rsquo"}, {353, "saron"}, {8218, "sbquo"}, {8901, "sdot"}, {167, "sect"}, {173, "shy"}, {963, "sigma"}, {962, "sigmaf"}, {8764, "sim"}, {9824, "spades"}, {8834, "sub"}, {8838, "sube"}, {8721, "sum"}, {8835, "sup"}, {185, "sup1"}, {178, "sup2"}, {179, "sup3"}, {8839, "supe"}, {223, "szlig"}, {964, "tau"}, {8221, "tdquo"}, {8756, "there4"}, {952, "theta"}, {977, "thetasym"}, {8201, "thinsp"}, {254, "thorn"}, {732, "tilde"}, {215, "times"}, {8482, "trade"}, {8657, "uArr"}, {250, "uacute"}, {8593, "uarr"}, {251, "ucirc"}, {249, "ugrave"}, {168, "uml"}, {978, "upsih"}, {965, "upsilon"}, {252, "uuml"}, {8472, "weierp"}, {958, "xi"}, {253, "yacute"}, {165, "yen"}, {255, "yuml"}, {950, "zeta"}, {8205, "zwj"}, {8204, "zwnj"}, }; // Sorted by |m_uCode|. const XFA_FMHtmlReserveCode kReservesForEncode[] = { {34, "quot"}, {38, "amp"}, {39, "apos"}, {60, "lt"}, {62, "gt"}, {160, "nbsp"}, {161, "iexcl"}, {162, "cent"}, {163, "pund"}, {164, "current"}, {165, "yen"}, {166, "brvbar"}, {167, "sect"}, {168, "uml"}, {169, "copy"}, {170, "ordf"}, {171, "laquo"}, {172, "not"}, {173, "shy"}, {174, "reg"}, {175, "macr"}, {176, "deg"}, {177, "plusmn"}, {178, "sup2"}, {179, "sup3"}, {180, "acute"}, {181, "micro"}, {182, "para"}, {183, "middot"}, {184, "cedil"}, {185, "sup1"}, {186, "ordm"}, {187, "raquo"}, {188, "frac14"}, {189, "frac12"}, {190, "frac34"}, {191, "iquest"}, {192, "Agrave"}, {193, "Aacute"}, {194, "Acirc"}, {195, "Atilde"}, {196, "Auml"}, {197, "Aring"}, {198, "AElig"}, {199, "Ccedil"}, {200, "Egrave"}, {201, "Eacute"}, {202, "Ecirc"}, {203, "Euml"}, {204, "lgrave"}, {205, "lacute"}, {206, "lcirc"}, {207, "luml"}, {208, "ETH"}, {209, "Ntilde"}, {210, "Ograve"}, {211, "Oacute"}, {212, "Ocirc"}, {213, "Otilde"}, {214, "Ouml"}, {215, "times"}, {216, "Oslash"}, {217, "Ugrave"}, {218, "Uacute"}, {219, "Ucirc"}, {220, "Uuml"}, {221, "Yacute"}, {222, "THORN"}, {223, "szlig"}, {224, "agrave"}, {225, "aacute"}, {226, "acirc"}, {227, "atilde"}, {228, "auml"}, {229, "aring"}, {230, "aelig"}, {231, "ccedil"}, {232, "egrave"}, {233, "eacute"}, {234, "ecirc"}, {235, "euml"}, {236, "igrave"}, {237, "iacute"}, {238, "icirc"}, {239, "iuml"}, {240, "eth"}, {241, "ntilde"}, {242, "ograve"}, {243, "oacute"}, {244, "ocirc"}, {245, "otilde"}, {246, "ouml"}, {247, "divide"}, {248, "oslash"}, {249, "ugrave"}, {250, "uacute"}, {251, "ucirc"}, {252, "uuml"}, {253, "yacute"}, {254, "thorn"}, {255, "yuml"}, {338, "OElig"}, {339, "oelig"}, {352, "Scaron"}, {353, "saron"}, {376, "Yuml"}, {402, "fnof"}, {710, "circ"}, {732, "tilde"}, {913, "Alpha"}, {914, "Beta"}, {915, "Gamma"}, {916, "Delta"}, {917, "Epsilon"}, {918, "Zeta"}, {919, "Eta"}, {920, "Theta"}, {921, "lota"}, {922, "Kappa"}, {923, "Lambda"}, {924, "Mu"}, {925, "Nu"}, {926, "Xi"}, {927, "Omicron"}, {928, "Pi"}, {929, "Rho"}, {931, "Sigma"}, {932, "Tau"}, {933, "Upsilon"}, {934, "Phi"}, {935, "Chi"}, {936, "Psi"}, {937, "Omega"}, {945, "alpha"}, {946, "beta"}, {947, "gamma"}, {948, "delta"}, {949, "epsilon"}, {950, "zeta"}, {951, "eta"}, {952, "theta"}, {953, "iota"}, {954, "kappa"}, {955, "lambda"}, {956, "mu"}, {957, "nu"}, {958, "xi"}, {959, "omicron"}, {960, "pi"}, {961, "rho"}, {962, "sigmaf"}, {963, "sigma"}, {964, "tau"}, {965, "upsilon"}, {966, "phi"}, {967, "chi"}, {968, "psi"}, {969, "omega"}, {977, "thetasym"}, {978, "upsih"}, {982, "piv"}, {8194, "ensp"}, {8195, "emsp"}, {8201, "thinsp"}, {8204, "zwnj"}, {8205, "zwj"}, {8206, "lrm"}, {8207, "rlm"}, {8211, "ndash"}, {8212, "mdash"}, {8216, "lsquo"}, {8217, "rsquo"}, {8218, "sbquo"}, {8220, "ldquo"}, {8221, "tdquo"}, {8222, "bdquo"}, {8224, "dagger"}, {8225, "Dagger"}, {8226, "bull"}, {8230, "hellip"}, {8240, "permil"}, {8242, "prime"}, {8249, "lsaquo"}, {8250, "rsaquo"}, {8254, "oline"}, {8260, "frasl"}, {8364, "euro"}, {8465, "image"}, {8472, "weierp"}, {8476, "real"}, {8482, "trade"}, {8501, "alefsym"}, {8592, "larr"}, {8593, "uarr"}, {8594, "rarr"}, {8595, "darr"}, {8596, "harr"}, {8629, "crarr"}, {8656, "lArr"}, {8657, "uArr"}, {8658, "rArr"}, {8659, "dArr"}, {8660, "hArr"}, {8704, "forall"}, {8706, "part"}, {8707, "exist"}, {8709, "empty"}, {8711, "nabla"}, {8712, "isin"}, {8713, "notin"}, {8715, "ni"}, {8719, "prod"}, {8721, "sum"}, {8722, "minus"}, {8727, "lowast"}, {8730, "radic"}, {8733, "prop"}, {8734, "infin"}, {8736, "ang"}, {8743, "and"}, {8744, "or"}, {8745, "cap"}, {8746, "cup"}, {8747, "int"}, {8756, "there4"}, {8764, "sim"}, {8773, "cong"}, {8776, "asymp"}, {8800, "ne"}, {8801, "equiv"}, {8804, "le"}, {8805, "ge"}, {8834, "sub"}, {8835, "sup"}, {8836, "nsub"}, {8838, "sube"}, {8839, "supe"}, {8853, "oplus"}, {8855, "otimes"}, {8869, "perp"}, {8901, "sdot"}, {8968, "lceil"}, {8969, "rceil"}, {8970, "lfloor"}, {8971, "rfloor"}, {9001, "lang"}, {9002, "rang"}, {9674, "loz"}, {9824, "spades"}, {9827, "clubs"}, {9829, "hearts"}, {9830, "diams"}, }; const FXJSE_FUNCTION_DESCRIPTOR kFormCalcFunctions[] = { {kFuncTag, "Abs", CFXJSE_FormCalcContext::Abs}, {kFuncTag, "Avg", CFXJSE_FormCalcContext::Avg}, {kFuncTag, "Ceil", CFXJSE_FormCalcContext::Ceil}, {kFuncTag, "Count", CFXJSE_FormCalcContext::Count}, {kFuncTag, "Floor", CFXJSE_FormCalcContext::Floor}, {kFuncTag, "Max", CFXJSE_FormCalcContext::Max}, {kFuncTag, "Min", CFXJSE_FormCalcContext::Min}, {kFuncTag, "Mod", CFXJSE_FormCalcContext::Mod}, {kFuncTag, "Round", CFXJSE_FormCalcContext::Round}, {kFuncTag, "Sum", CFXJSE_FormCalcContext::Sum}, {kFuncTag, "Date", CFXJSE_FormCalcContext::Date}, {kFuncTag, "Date2Num", CFXJSE_FormCalcContext::Date2Num}, {kFuncTag, "DateFmt", CFXJSE_FormCalcContext::DateFmt}, {kFuncTag, "IsoDate2Num", CFXJSE_FormCalcContext::IsoDate2Num}, {kFuncTag, "IsoTime2Num", CFXJSE_FormCalcContext::IsoTime2Num}, {kFuncTag, "LocalDateFmt", CFXJSE_FormCalcContext::LocalDateFmt}, {kFuncTag, "LocalTimeFmt", CFXJSE_FormCalcContext::LocalTimeFmt}, {kFuncTag, "Num2Date", CFXJSE_FormCalcContext::Num2Date}, {kFuncTag, "Num2GMTime", CFXJSE_FormCalcContext::Num2GMTime}, {kFuncTag, "Num2Time", CFXJSE_FormCalcContext::Num2Time}, {kFuncTag, "Time", CFXJSE_FormCalcContext::Time}, {kFuncTag, "Time2Num", CFXJSE_FormCalcContext::Time2Num}, {kFuncTag, "TimeFmt", CFXJSE_FormCalcContext::TimeFmt}, {kFuncTag, "Apr", CFXJSE_FormCalcContext::Apr}, {kFuncTag, "Cterm", CFXJSE_FormCalcContext::CTerm}, {kFuncTag, "FV", CFXJSE_FormCalcContext::FV}, {kFuncTag, "Ipmt", CFXJSE_FormCalcContext::IPmt}, {kFuncTag, "NPV", CFXJSE_FormCalcContext::NPV}, {kFuncTag, "Pmt", CFXJSE_FormCalcContext::Pmt}, {kFuncTag, "PPmt", CFXJSE_FormCalcContext::PPmt}, {kFuncTag, "PV", CFXJSE_FormCalcContext::PV}, {kFuncTag, "Rate", CFXJSE_FormCalcContext::Rate}, {kFuncTag, "Term", CFXJSE_FormCalcContext::Term}, {kFuncTag, "Choose", CFXJSE_FormCalcContext::Choose}, {kFuncTag, "Exists", CFXJSE_FormCalcContext::Exists}, {kFuncTag, "HasValue", CFXJSE_FormCalcContext::HasValue}, {kFuncTag, "Oneof", CFXJSE_FormCalcContext::Oneof}, {kFuncTag, "Within", CFXJSE_FormCalcContext::Within}, {kFuncTag, "If", CFXJSE_FormCalcContext::If}, {kFuncTag, "Eval", CFXJSE_FormCalcContext::Eval}, {kFuncTag, "Translate", CFXJSE_FormCalcContext::eval_translation}, {kFuncTag, "Ref", CFXJSE_FormCalcContext::Ref}, {kFuncTag, "UnitType", CFXJSE_FormCalcContext::UnitType}, {kFuncTag, "UnitValue", CFXJSE_FormCalcContext::UnitValue}, {kFuncTag, "At", CFXJSE_FormCalcContext::At}, {kFuncTag, "Concat", CFXJSE_FormCalcContext::Concat}, {kFuncTag, "Decode", CFXJSE_FormCalcContext::Decode}, {kFuncTag, "Encode", CFXJSE_FormCalcContext::Encode}, {kFuncTag, "Format", CFXJSE_FormCalcContext::Format}, {kFuncTag, "Left", CFXJSE_FormCalcContext::Left}, {kFuncTag, "Len", CFXJSE_FormCalcContext::Len}, {kFuncTag, "Lower", CFXJSE_FormCalcContext::Lower}, {kFuncTag, "Ltrim", CFXJSE_FormCalcContext::Ltrim}, {kFuncTag, "Parse", CFXJSE_FormCalcContext::Parse}, {kFuncTag, "Replace", CFXJSE_FormCalcContext::Replace}, {kFuncTag, "Right", CFXJSE_FormCalcContext::Right}, {kFuncTag, "Rtrim", CFXJSE_FormCalcContext::Rtrim}, {kFuncTag, "Space", CFXJSE_FormCalcContext::Space}, {kFuncTag, "Str", CFXJSE_FormCalcContext::Str}, {kFuncTag, "Stuff", CFXJSE_FormCalcContext::Stuff}, {kFuncTag, "Substr", CFXJSE_FormCalcContext::Substr}, {kFuncTag, "Uuid", CFXJSE_FormCalcContext::Uuid}, {kFuncTag, "Upper", CFXJSE_FormCalcContext::Upper}, {kFuncTag, "WordNum", CFXJSE_FormCalcContext::WordNum}, {kFuncTag, "Get", CFXJSE_FormCalcContext::Get}, {kFuncTag, "Post", CFXJSE_FormCalcContext::Post}, {kFuncTag, "Put", CFXJSE_FormCalcContext::Put}, {kFuncTag, "pos_op", CFXJSE_FormCalcContext::positive_operator}, {kFuncTag, "neg_op", CFXJSE_FormCalcContext::negative_operator}, {kFuncTag, "log_or_op", CFXJSE_FormCalcContext::logical_or_operator}, {kFuncTag, "log_and_op", CFXJSE_FormCalcContext::logical_and_operator}, {kFuncTag, "log_not_op", CFXJSE_FormCalcContext::logical_not_operator}, {kFuncTag, "eq_op", CFXJSE_FormCalcContext::equality_operator}, {kFuncTag, "neq_op", CFXJSE_FormCalcContext::notequality_operator}, {kFuncTag, "lt_op", CFXJSE_FormCalcContext::less_operator}, {kFuncTag, "le_op", CFXJSE_FormCalcContext::lessequal_operator}, {kFuncTag, "gt_op", CFXJSE_FormCalcContext::greater_operator}, {kFuncTag, "ge_op", CFXJSE_FormCalcContext::greaterequal_operator}, {kFuncTag, "plus_op", CFXJSE_FormCalcContext::plus_operator}, {kFuncTag, "minus_op", CFXJSE_FormCalcContext::minus_operator}, {kFuncTag, "mul_op", CFXJSE_FormCalcContext::multiple_operator}, {kFuncTag, "div_op", CFXJSE_FormCalcContext::divide_operator}, {kFuncTag, "asgn_val_op", CFXJSE_FormCalcContext::assign_value_operator}, {kFuncTag, "dot_acc", CFXJSE_FormCalcContext::dot_accessor}, {kFuncTag, "dotdot_acc", CFXJSE_FormCalcContext::dotdot_accessor}, {kFuncTag, "concat_obj", CFXJSE_FormCalcContext::concat_fm_object}, {kFuncTag, "is_obj", CFXJSE_FormCalcContext::is_fm_object}, {kFuncTag, "is_ary", CFXJSE_FormCalcContext::is_fm_array}, {kFuncTag, "get_val", CFXJSE_FormCalcContext::get_fm_value}, {kFuncTag, "get_jsobj", CFXJSE_FormCalcContext::get_fm_jsobj}, {kFuncTag, "var_filter", CFXJSE_FormCalcContext::fm_var_filter}, }; const uint8_t kAltTableDate[] = { 255, 255, 255, 3, 9, 255, 255, 255, 255, 255, 255, 255, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 255, 255, 255, 255, 255, 255, 255, 255, }; static_assert(std::size(kAltTableDate) == L'a' - L'A' + 1, "Invalid kAltTableDate size."); const uint8_t kAltTableTime[] = { 14, 255, 255, 3, 9, 255, 255, 15, 255, 255, 255, 255, 6, 255, 255, 255, 255, 255, 7, 255, 255, 255, 255, 255, 1, 17, 255, 255, 255, 255, 255, 255, 255, }; static_assert(std::size(kAltTableTime) == L'a' - L'A' + 1, "Invalid kAltTableTime size."); void AlternateDateTimeSymbols(WideString* pPattern, const WideString& wsAltSymbols, bool bIsDate) { const uint8_t* pAltTable = bIsDate ? kAltTableDate : kAltTableTime; int32_t nLength = pPattern->GetLength(); bool bInConstRange = false; bool bEscape = false; int32_t i = 0; UNSAFE_TODO({ while (i < nLength) { wchar_t wc = (*pPattern)[i]; if (wc == L'\'') { bInConstRange = !bInConstRange; if (bEscape) { i++; } else { pPattern->Delete(i); nLength--; } bEscape = !bEscape; continue; } if (!bInConstRange && wc >= L'A' && wc <= L'a') { uint8_t nAlt = pAltTable[wc - L'A']; if (nAlt != 255) { pPattern->SetAt(i, wsAltSymbols[nAlt]); } } i++; bEscape = false; } }); } std::pair PatternStringType( ByteStringView bsPattern) { WideString wsPattern = WideString::FromUTF8(bsPattern); if (wsPattern.First(8).EqualsASCII("datetime")) { return {true, CXFA_LocaleValue::ValueType::kDateTime}; } if (wsPattern.First(4).EqualsASCII("date")) { auto pos = wsPattern.Find(L"time"); if (pos.has_value() && pos.value() != 0) { return {true, CXFA_LocaleValue::ValueType::kDateTime}; } return {true, CXFA_LocaleValue::ValueType::kDate}; } if (wsPattern.First(4).EqualsASCII("time")) { return {true, CXFA_LocaleValue::ValueType::kTime}; } if (wsPattern.First(4).EqualsASCII("text")) { return {true, CXFA_LocaleValue::ValueType::kText}; } if (wsPattern.First(3).EqualsASCII("num")) { if (wsPattern.Substr(4, 7).EqualsASCII("integer")) { return {true, CXFA_LocaleValue::ValueType::kInteger}; } if (wsPattern.Substr(4, 7).EqualsASCII("decimal")) { return {true, CXFA_LocaleValue::ValueType::kDecimal}; } if (wsPattern.Substr(4, 8).EqualsASCII("currency")) { return {true, CXFA_LocaleValue::ValueType::kFloat}; } if (wsPattern.Substr(4, 7).EqualsASCII("percent")) { return {true, CXFA_LocaleValue::ValueType::kFloat}; } return {true, CXFA_LocaleValue::ValueType::kFloat}; } CXFA_LocaleValue::ValueType type = CXFA_LocaleValue::ValueType::kNull; wsPattern.MakeLower(); const wchar_t* pData = wsPattern.c_str(); int32_t iLength = wsPattern.GetLength(); int32_t iIndex = 0; bool bSingleQuotation = false; UNSAFE_TODO({ while (iIndex < iLength) { wchar_t wsPatternChar = pData[iIndex]; if (wsPatternChar == 0x27) { bSingleQuotation = !bSingleQuotation; iIndex++; continue; } if (bSingleQuotation) { iIndex++; continue; } if (wsPatternChar == 'h' || wsPatternChar == 'k') { return {false, CXFA_LocaleValue::ValueType::kTime}; } if (wsPatternChar == 'x' || wsPatternChar == 'o' || wsPatternChar == '0') { return {false, CXFA_LocaleValue::ValueType::kText}; } if (wsPatternChar == 'v' || wsPatternChar == '8' || wsPatternChar == '$') { return {false, CXFA_LocaleValue::ValueType::kFloat}; } if (wsPatternChar == 'y' || wsPatternChar == 'j') { iIndex++; wchar_t timePatternChar; while (iIndex < iLength) { timePatternChar = pData[iIndex]; if (timePatternChar == 0x27) { bSingleQuotation = !bSingleQuotation; iIndex++; continue; } if (!bSingleQuotation && timePatternChar == 't') { return {false, CXFA_LocaleValue::ValueType::kDateTime}; } iIndex++; } return {false, CXFA_LocaleValue::ValueType::kDate}; } if (wsPatternChar == 'a') { type = CXFA_LocaleValue::ValueType::kText; } else if (wsPatternChar == 'z' || wsPatternChar == 's' || wsPatternChar == 'e' || wsPatternChar == ',' || wsPatternChar == '.') { type = CXFA_LocaleValue::ValueType::kFloat; } iIndex++; } return {false, type}; }); } CFXJSE_FormCalcContext* ToFormCalcContext(CFXJSE_HostObject* pHostObj) { return pHostObj ? pHostObj->AsFormCalcContext() : nullptr; } GCedLocaleIface* LocaleFromString(CXFA_Document* pDoc, CXFA_LocaleMgr* pMgr, ByteStringView bsLocale) { if (!bsLocale.IsEmpty()) return pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale)); CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject()); return pThisNode->GetLocale(); } WideString FormatFromString(LocaleIface* pLocale, ByteStringView bsFormat) { if (!bsFormat.IsEmpty()) return WideString::FromUTF8(bsFormat); return pLocale->GetDatePattern(LocaleIface::DateTimeSubcategory::kDefault); } LocaleIface::DateTimeSubcategory SubCategoryFromInt(int32_t iStyle) { switch (iStyle) { case 1: return LocaleIface::DateTimeSubcategory::kShort; case 3: return LocaleIface::DateTimeSubcategory::kLong; case 4: return LocaleIface::DateTimeSubcategory::kFull; case 0: case 2: default: return LocaleIface::DateTimeSubcategory::kMedium; } } ByteString GetLocalDateTimeFormat(CXFA_Document* pDoc, int32_t iStyle, ByteStringView bsLocale, bool bStandard, bool bIsDate) { CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); LocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); if (!pLocale) return ByteString(); LocaleIface::DateTimeSubcategory category = SubCategoryFromInt(iStyle); WideString wsLocal = bIsDate ? pLocale->GetDatePattern(category) : pLocale->GetTimePattern(category); if (!bStandard) AlternateDateTimeSymbols(&wsLocal, pLocale->GetDateTimeSymbols(), bIsDate); return wsLocal.ToUTF8(); } bool IsWhitespace(char c) { return c == 0x20 || c == 0x09 || c == 0x0B || c == 0x0C || c == 0x0A || c == 0x0D; } bool IsPartOfNumber(char ch) { return isdigit(ch) || ch == '-' || ch == '.'; } bool IsPartOfNumberW(wchar_t ch) { return FXSYS_IsDecimalDigit(ch) || ch == L'-' || ch == L'.'; } ByteString GUIDString(bool bSeparator) { std::array data; FX_Random_GenerateMT(fxcrt::reinterpret_span(data)); data[6] = (data[6] & 0x0F) | 0x40; ByteString bsGUID; { // Span's lifetime must end before ReleaseBuffer() below. pdfium::span pBuf = bsGUID.GetBuffer(40); size_t out_index = 0; for (size_t i = 0; i < 16; ++i, out_index += 2) { if (bSeparator && (i == 4 || i == 6 || i == 8 || i == 10)) { pBuf[out_index++] = L'-'; } FXSYS_IntToTwoHexChars(data[i], &pBuf[out_index]); } } bsGUID.ReleaseBuffer(bSeparator ? 36 : 32); return bsGUID; } void GetLocalTimeZone(int32_t* pHour, int32_t* pMin, int32_t* pSec) { time_t now; FXSYS_time(&now); struct tm* pGmt = gmtime(&now); struct tm* pLocal = FXSYS_localtime(&now); *pHour = pLocal->tm_hour - pGmt->tm_hour; *pMin = pLocal->tm_min - pGmt->tm_min; *pSec = pLocal->tm_sec - pGmt->tm_sec; } bool HTMLSTR2Code(const WideString& pData, uint32_t* iCode) { auto cmpFunc = [](const XFA_FMHtmlReserveCode& iter, ByteStringView val) { return strcmp(val.unterminated_c_str(), iter.m_htmlReserve) > 0; }; if (!pData.IsASCII()) return false; ByteString temp = pData.ToASCII(); const XFA_FMHtmlReserveCode* result = std::lower_bound( std::begin(kReservesForDecode), std::end(kReservesForDecode), temp.AsStringView(), cmpFunc); if (result != std::end(kReservesForDecode) && !strcmp(temp.c_str(), result->m_htmlReserve)) { *iCode = result->m_uCode; return true; } return false; } bool HTMLCode2STR(uint32_t iCode, WideString* wsHTMLReserve) { auto cmpFunc = [](const XFA_FMHtmlReserveCode iter, const uint32_t& val) { return iter.m_uCode < val; }; const XFA_FMHtmlReserveCode* result = std::lower_bound(std::begin(kReservesForEncode), std::end(kReservesForEncode), iCode, cmpFunc); if (result != std::end(kReservesForEncode) && result->m_uCode == iCode) { *wsHTMLReserve = WideString::FromASCII(result->m_htmlReserve); return true; } return false; } WideString DecodeURL(const WideString& wsURL) { const wchar_t* pData = wsURL.c_str(); size_t iLen = wsURL.GetLength(); WideTextBuffer wsResultBuf; UNSAFE_TODO({ for (size_t i = 0; i < iLen; ++i) { wchar_t ch = pData[i]; if ('%' != ch) { wsResultBuf.AppendChar(ch); continue; } wchar_t chTemp = 0; int32_t iCount = 0; while (iCount < 2) { if (++i >= iLen) { return WideString(); } chTemp *= 16; ch = pData[i]; if (!FXSYS_IsWideHexDigit(ch)) { return WideString(); } chTemp += FXSYS_WideHexCharToInt(ch); ++iCount; } wsResultBuf.AppendChar(chTemp); } return wsResultBuf.MakeString(); }); } WideString DecodeMLInternal(const WideString& wsHTML, bool bIsHTML) { const wchar_t* pData = wsHTML.c_str(); size_t iLen = wsHTML.GetLength(); WideTextBuffer wsResultBuf; UNSAFE_TODO({ for (size_t i = 0; i < iLen; ++i) { wchar_t ch = pData[i]; if (ch != '&') { wsResultBuf.AppendChar(ch); continue; } if (++i >= iLen) break; ch = pData[i]; if (ch == '#') { if (++i >= iLen) { break; } ch = pData[i]; if (ch != 'x' && ch != 'X') { return WideString(); } if (++i >= iLen) break; ch = pData[i]; uint32_t iCode = 0; while (ch != ';' && i < iLen) { iCode *= 16; if (!FXSYS_IsWideHexDigit(ch)) { return WideString(); } iCode += FXSYS_WideHexCharToInt(ch); if (++i >= iLen) { break; } ch = pData[i]; } wsResultBuf.AppendChar(iCode); continue; } wchar_t szBuffer[9]; size_t iStrIndex = 0; while (ch != ';' && i < iLen) { if (iStrIndex < 8) { szBuffer[iStrIndex++] = ch; } if (++i >= iLen) { break; } ch = pData[i]; } szBuffer[iStrIndex] = 0; if (bIsHTML) { uint32_t iData = 0; if (HTMLSTR2Code(szBuffer, &iData)) { wsResultBuf.AppendChar((wchar_t)iData); } } else { if (wcscmp(szBuffer, L"quot") == 0) { wsResultBuf.AppendChar('"'); } else if (wcscmp(szBuffer, L"amp") == 0) { wsResultBuf.AppendChar('&'); } else if (wcscmp(szBuffer, L"apos") == 0) { wsResultBuf.AppendChar('\''); } else if (wcscmp(szBuffer, L"lt") == 0) { wsResultBuf.AppendChar('<'); } else if (wcscmp(szBuffer, L"gt") == 0) { wsResultBuf.AppendChar('>'); } } } return wsResultBuf.MakeString(); }); } WideString DecodeHTML(const WideString& wsHTML) { return DecodeMLInternal(wsHTML, true); } WideString DecodeXML(const WideString& wsXML) { return DecodeMLInternal(wsXML, false); } WideString EncodeURL(const ByteString& bsURL) { static constexpr char32_t kStrUnsafe[] = {' ', '<', '>', '"', '#', '%', '{', '}', '|', '\\', '^', '~', '[', ']', '`'}; static constexpr char32_t kStrReserved[] = {';', '/', '?', ':', '@', '=', '&'}; WideString wsURL = WideString::FromUTF8(bsURL.AsStringView()); WideTextBuffer wsResultBuf; std::array encode_buffer = {L'%'}; // Starts with %. for (char32_t ch : pdfium::CodePointView(wsURL.AsStringView())) { if (ch <= 0x1f || (ch >= 0x7f && ch <= 0xff) || pdfium::Contains(kStrUnsafe, ch) || pdfium::Contains(kStrReserved, ch)) { int32_t iIndex = ch / 16; encode_buffer[1] = kStrCode[iIndex]; encode_buffer[2] = kStrCode[ch - iIndex * 16]; wsResultBuf << WideStringView(encode_buffer); continue; } if (ch >= 0x20 && ch <= 0x7e) { wsResultBuf.AppendChar(ch); continue; } const wchar_t iRadix = 16; WideString wsBuffer; while (ch >= iRadix) { wchar_t tmp = kStrCode[ch % iRadix]; ch /= iRadix; wsBuffer += tmp; } wsBuffer += kStrCode[ch]; int32_t iLen = wsBuffer.GetLength(); if (iLen < 2) { break; } int32_t iIndex = 0; if (iLen % 2 != 0) { encode_buffer[1] = '0'; encode_buffer[2] = wsBuffer[iLen - 1]; iIndex = iLen - 2; } else { encode_buffer[1] = wsBuffer[iLen - 1]; encode_buffer[2] = wsBuffer[iLen - 2]; iIndex = iLen - 3; } wsResultBuf << WideStringView(encode_buffer); while (iIndex > 0) { encode_buffer[1] = wsBuffer[iIndex]; encode_buffer[2] = wsBuffer[iIndex - 1]; iIndex -= 2; wsResultBuf << WideStringView(encode_buffer); } } return wsResultBuf.MakeString(); } WideString EncodeHTML(const ByteString& bsHTML) { WideString wsHTML = WideString::FromUTF8(bsHTML.AsStringView()); std::array encode_buffer; encode_buffer[0] = '&'; encode_buffer[1] = '#'; encode_buffer[2] = 'x'; WideTextBuffer wsResultBuf; for (char32_t ch : pdfium::CodePointView(wsHTML.AsStringView())) { WideString htmlReserve; if (HTMLCode2STR(ch, &htmlReserve)) { wsResultBuf.AppendChar(L'&'); wsResultBuf << htmlReserve; wsResultBuf.AppendChar(L';'); } else if (ch >= 32 && ch <= 126) { wsResultBuf.AppendChar(static_cast(ch)); } else if (ch < 256) { int32_t iIndex = ch / 16; encode_buffer[3] = kStrCode[iIndex]; encode_buffer[4] = kStrCode[ch - iIndex * 16]; encode_buffer[5] = ';'; wsResultBuf << WideStringView(encode_buffer).First(6); } else if (ch < 65536) { int32_t iBigByte = ch / 256; int32_t iLittleByte = ch % 256; encode_buffer[3] = kStrCode[iBigByte / 16]; encode_buffer[4] = kStrCode[iBigByte % 16]; encode_buffer[5] = kStrCode[iLittleByte / 16]; encode_buffer[6] = kStrCode[iLittleByte % 16]; encode_buffer[7] = ';'; wsResultBuf << WideStringView(encode_buffer).First(8); } else { // TODO(tsepez): Handle codepoint not in BMP. } } return wsResultBuf.MakeString(); } WideString EncodeXML(const ByteString& bsXML) { WideString wsXML = WideString::FromUTF8(bsXML.AsStringView()); WideTextBuffer wsResultBuf; std::array encode_buffer; encode_buffer[0] = '&'; encode_buffer[1] = '#'; encode_buffer[2] = 'x'; for (char32_t ch : pdfium::CodePointView(wsXML.AsStringView())) { switch (ch) { case '"': wsResultBuf.AppendChar('&'); wsResultBuf << WideStringView(L"quot"); wsResultBuf.AppendChar(';'); break; case '&': wsResultBuf.AppendChar('&'); wsResultBuf << WideStringView(L"amp"); wsResultBuf.AppendChar(';'); break; case '\'': wsResultBuf.AppendChar('&'); wsResultBuf << WideStringView(L"apos"); wsResultBuf.AppendChar(';'); break; case '<': wsResultBuf.AppendChar('&'); wsResultBuf << WideStringView(L"lt"); wsResultBuf.AppendChar(';'); break; case '>': wsResultBuf.AppendChar('&'); wsResultBuf << WideStringView(L"gt"); wsResultBuf.AppendChar(';'); break; default: { if (ch >= 32 && ch <= 126) { wsResultBuf.AppendChar(static_cast(ch)); } else if (ch < 256) { int32_t iIndex = ch / 16; encode_buffer[3] = kStrCode[iIndex]; encode_buffer[4] = kStrCode[ch - iIndex * 16]; encode_buffer[5] = ';'; wsResultBuf << WideStringView(encode_buffer).First(6); } else if (ch < 65536) { int32_t iBigByte = ch / 256; int32_t iLittleByte = ch % 256; encode_buffer[3] = kStrCode[iBigByte / 16]; encode_buffer[4] = kStrCode[iBigByte % 16]; encode_buffer[5] = kStrCode[iLittleByte / 16]; encode_buffer[6] = kStrCode[iLittleByte % 16]; encode_buffer[7] = ';'; wsResultBuf << WideStringView(encode_buffer).First(8); } else { // TODO(tsepez): Handle codepoint not in BMP. } break; } } } return wsResultBuf.MakeString(); } ByteString TrillionUS(ByteStringView bsData) { static const char kUnits[][6] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"}; static const char kCapUnits[][6] = {"Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"}; static const char kTens[][10] = { "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}; static const char kLastTens[][8] = {"Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}; static const char kComm[][11] = {" Hundred ", " Thousand ", " Million ", " Billion ", "Trillion"}; const char* pData = bsData.unterminated_c_str(); int32_t iLength = bsData.GetLength(); int32_t iComm = 0; if (iLength > 12) iComm = 4; else if (iLength > 9) iComm = 3; else if (iLength > 6) iComm = 2; else if (iLength > 3) iComm = 1; int32_t iFirstCount = iLength % 3; if (iFirstCount == 0) iFirstCount = 3; ByteString strBuf; int32_t iIndex = 0; UNSAFE_TODO({ if (iFirstCount == 3) { if (pData[iIndex] != '0') { strBuf += kCapUnits[pData[iIndex] - '0']; strBuf += kComm[0]; } if (pData[iIndex + 1] == '0') { strBuf += kCapUnits[pData[iIndex + 2] - '0']; } else { if (pData[iIndex + 1] > '1') { strBuf += kLastTens[pData[iIndex + 1] - '2']; strBuf += "-"; strBuf += kUnits[pData[iIndex + 2] - '0']; } else if (pData[iIndex + 1] == '1') { strBuf += kTens[pData[iIndex + 2] - '0']; } else if (pData[iIndex + 1] == '0') { strBuf += kCapUnits[pData[iIndex + 2] - '0']; } } iIndex += 3; } else if (iFirstCount == 2) { if (pData[iIndex] == '0') { strBuf += kCapUnits[pData[iIndex + 1] - '0']; } else { if (pData[iIndex] > '1') { strBuf += kLastTens[pData[iIndex] - '2']; strBuf += "-"; strBuf += kUnits[pData[iIndex + 1] - '0']; } else if (pData[iIndex] == '1') { strBuf += kTens[pData[iIndex + 1] - '0']; } else if (pData[iIndex] == '0') { strBuf += kCapUnits[pData[iIndex + 1] - '0']; } } iIndex += 2; } else if (iFirstCount == 1) { strBuf += kCapUnits[pData[iIndex] - '0']; ++iIndex; } if (iLength > 3 && iFirstCount > 0) { strBuf += kComm[iComm]; --iComm; } while (iIndex < iLength) { if (pData[iIndex] != '0') { strBuf += kCapUnits[pData[iIndex] - '0']; strBuf += kComm[0]; } if (pData[iIndex + 1] == '0') { strBuf += kCapUnits[pData[iIndex + 2] - '0']; } else { if (pData[iIndex + 1] > '1') { strBuf += kLastTens[pData[iIndex + 1] - '2']; strBuf += "-"; strBuf += kUnits[pData[iIndex + 2] - '0']; } else if (pData[iIndex + 1] == '1') { strBuf += kTens[pData[iIndex + 2] - '0']; } else if (pData[iIndex + 1] == '0') { strBuf += kCapUnits[pData[iIndex + 2] - '0']; } } if (iIndex < iLength - 3) { strBuf += kComm[iComm]; --iComm; } iIndex += 3; } return strBuf; }); } ByteString WordUS(ByteStringView bsData, int32_t iStyle) { if (iStyle < 0 || iStyle > 2) return ByteString(); int32_t iLength = bsData.GetLength(); ByteString strBuf; int32_t iIndex = 0; while (iIndex < iLength) { if (bsData[iIndex] == '.') break; ++iIndex; } int32_t iInteger = iIndex; iIndex = 0; while (iIndex < iInteger) { int32_t iCount = (iInteger - iIndex) % 12; if (!iCount && iInteger - iIndex > 0) iCount = 12; strBuf += TrillionUS(bsData.Substr(iIndex, iCount)); iIndex += iCount; if (iIndex < iInteger) strBuf += " Trillion "; } if (iStyle > 0) strBuf += " Dollars"; if (iStyle > 1 && iInteger < iLength) { strBuf += " And "; iIndex = iInteger + 1; while (iIndex < iLength) { int32_t iCount = (iLength - iIndex) % 12; if (!iCount && iLength - iIndex > 0) iCount = 12; strBuf += TrillionUS(bsData.Substr(iIndex, iCount)); iIndex += iCount; if (iIndex < iLength) strBuf += " Trillion "; } strBuf += " Cents"; } return strBuf; } v8::Local GetObjectDefaultValue(v8::Isolate* pIsolate, v8::Local pObject) { CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject)); if (!pNode) return fxv8::NewNullHelper(pIsolate); v8::Local value; pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &value, false, XFA_Attribute::Unknown); return value; } bool SetObjectDefaultValue(v8::Isolate* pIsolate, v8::Local pObject, v8::Local hNewValue) { CXFA_Node* pNode = ToNode(CFXJSE_Engine::ToObject(pIsolate, pObject)); if (!pNode) return false; pNode->JSObject()->ScriptSomDefaultValue(pIsolate, &hNewValue, true, XFA_Attribute::Unknown); return true; } v8::Local GetExtractedValue(v8::Isolate* pIsolate, v8::Local pValue) { if (pValue.IsEmpty()) return v8::Local(); if (fxv8::IsArray(pValue)) { v8::Local arr = pValue.As(); uint32_t iLength = fxv8::GetArrayLengthHelper(arr); if (iLength < 3) return fxv8::NewUndefinedHelper(pIsolate); v8::Local propertyValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1); v8::Local jsValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2); if (!fxv8::IsObject(jsValue)) return fxv8::NewUndefinedHelper(pIsolate); v8::Local jsObjectValue = jsValue.As(); if (fxv8::IsNull(propertyValue)) return GetObjectDefaultValue(pIsolate, jsObjectValue); ByteString bsName = fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue); return fxv8::ReentrantGetObjectPropertyHelper(pIsolate, jsObjectValue, bsName.AsStringView()); } if (fxv8::IsObject(pValue)) return GetObjectDefaultValue(pIsolate, pValue.As()); return pValue; } v8::Local GetSimpleValue( const v8::FunctionCallbackInfo& info, uint32_t index) { DCHECK(index < static_cast(info.Length())); return GetExtractedValue(info.GetIsolate(), info[index]); } bool ValueIsNull(v8::Isolate* pIsolate, v8::Local arg) { v8::Local extracted = GetExtractedValue(pIsolate, arg); return extracted.IsEmpty() || fxv8::IsNull(extracted); } int32_t ValueToInteger(v8::Isolate* pIsolate, v8::Local arg) { v8::Local extracted = GetExtractedValue(pIsolate, arg); if (extracted.IsEmpty()) return 0; if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted)) return ValueToInteger(pIsolate, extracted); if (fxv8::IsString(extracted)) { ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted); return FXSYS_atoi(bsValue.c_str()); } return fxv8::ReentrantToInt32Helper(pIsolate, extracted); } float ValueToFloat(v8::Isolate* pIsolate, v8::Local arg) { v8::Local extracted = GetExtractedValue(pIsolate, arg); if (extracted.IsEmpty()) return 0.0f; if (fxv8::IsUndefined(extracted)) return 0.0f; if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted)) return ValueToFloat(pIsolate, extracted); if (fxv8::IsString(extracted)) { ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted); return strtof(bsValue.c_str(), nullptr); } return fxv8::ReentrantToFloatHelper(pIsolate, extracted); } double ValueToDouble(v8::Isolate* pIsolate, v8::Local arg) { v8::Local extracted = GetExtractedValue(pIsolate, arg); if (extracted.IsEmpty()) return 0.0; if (fxv8::IsUndefined(extracted)) return 0.0; if (fxv8::IsObject(extracted) || fxv8::IsArray(extracted)) return ValueToDouble(pIsolate, extracted); if (fxv8::IsString(extracted)) { ByteString bsValue = fxv8::ReentrantToByteStringHelper(pIsolate, extracted); return strtod(bsValue.c_str(), nullptr); } return fxv8::ReentrantToDoubleHelper(pIsolate, extracted); } std::optional ExtractDouble(v8::Isolate* pIsolate, v8::Local src) { if (src.IsEmpty()) return 0.0; if (!fxv8::IsArray(src)) return ValueToDouble(pIsolate, src); v8::Local arr = src.As(); uint32_t iLength = fxv8::GetArrayLengthHelper(arr); if (iLength < 3) return std::nullopt; v8::Local propertyValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1); v8::Local jsValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 2); if (fxv8::IsNull(propertyValue) || !fxv8::IsObject(jsValue)) return ValueToDouble(pIsolate, jsValue); ByteString bsName = fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue); return ValueToDouble( pIsolate, fxv8::ReentrantGetObjectPropertyHelper( pIsolate, jsValue.As(), bsName.AsStringView())); } ByteString ValueToUTF8String(v8::Isolate* pIsolate, v8::Local arg) { if (arg.IsEmpty()) return ByteString(); if (fxv8::IsNull(arg) || fxv8::IsUndefined(arg)) return ByteString(); if (fxv8::IsBoolean(arg)) return fxv8::ReentrantToBooleanHelper(pIsolate, arg) ? "1" : "0"; return fxv8::ReentrantToByteStringHelper(pIsolate, arg); } bool SimpleValueCompare(v8::Isolate* pIsolate, v8::Local firstValue, v8::Local secondValue) { if (firstValue.IsEmpty()) return false; if (fxv8::IsString(firstValue)) { const ByteString first = ValueToUTF8String(pIsolate, firstValue); const ByteString second = ValueToUTF8String(pIsolate, secondValue); return first == second; } if (fxv8::IsNumber(firstValue)) { const float first = ValueToFloat(pIsolate, firstValue); const float second = ValueToFloat(pIsolate, secondValue); return first == second; } if (fxv8::IsBoolean(firstValue)) { const bool first = fxv8::ReentrantToBooleanHelper(pIsolate, firstValue); const bool second = fxv8::ReentrantToBooleanHelper(pIsolate, secondValue); return first == second; } return fxv8::IsNull(firstValue) && fxv8::IsNull(secondValue); } v8::LocalVector UnfoldArgs( const v8::FunctionCallbackInfo& info) { v8::LocalVector results(info.GetIsolate()); v8::Isolate* pIsolate = info.GetIsolate(); for (int i = 1; i < info.Length(); ++i) { v8::Local arg = info[i]; if (fxv8::IsArray(arg)) { v8::Local arr = arg.As(); uint32_t iLength = fxv8::GetArrayLengthHelper(arr); if (iLength < 3) continue; v8::Local propertyValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1); for (uint32_t j = 2; j < iLength; j++) { v8::Local jsValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, j); if (!fxv8::IsObject(jsValue)) { results.push_back(fxv8::NewUndefinedHelper(pIsolate)); } else if (fxv8::IsNull(propertyValue)) { results.push_back( GetObjectDefaultValue(pIsolate, jsValue.As())); } else { ByteString bsName = fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue); results.push_back(fxv8::ReentrantGetObjectPropertyHelper( pIsolate, jsValue.As(), bsName.AsStringView())); } } } else if (fxv8::IsObject(arg)) { results.push_back(GetObjectDefaultValue(pIsolate, arg.As())); } else { results.push_back(arg); } } return results; } // Returns empty value on failure. v8::Local GetObjectForName(CFXJSE_HostObject* pHostObject, ByteStringView bsAccessorName) { CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument(); if (!pDoc) return v8::Local(); CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); std::optional maybeResult = pScriptContext->ResolveObjects( pScriptContext->GetThisObject(), WideString::FromUTF8(bsAccessorName).AsStringView(), Mask{ XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kProperties, XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent}); if (!maybeResult.has_value() || maybeResult.value().type != CFXJSE_Engine::ResolveResult::Type::kNodes || maybeResult.value().objects.empty()) { return v8::Local(); } return pScriptContext->GetOrCreateJSBindingFromMap( maybeResult.value().objects.front().Get()); } std::optional ResolveObjects( CFXJSE_HostObject* pHostObject, v8::Local pRefValue, ByteStringView bsSomExp, bool bDotAccessor, bool bHasNoResolveName) { CXFA_Document* pDoc = ToFormCalcContext(pHostObject)->GetDocument(); if (!pDoc) return std::nullopt; v8::Isolate* pIsolate = ToFormCalcContext(pHostObject)->GetIsolate(); WideString wsSomExpression = WideString::FromUTF8(bsSomExp); CFXJSE_Engine* pScriptContext = pDoc->GetScriptContext(); CXFA_Object* pNode = nullptr; Mask dwFlags; if (bDotAccessor) { if (fxv8::IsNull(pRefValue)) { pNode = pScriptContext->GetThisObject(); dwFlags = {XFA_ResolveFlag::kSiblings, XFA_ResolveFlag::kParent}; } else { pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue); if (!pNode) return std::nullopt; if (bHasNoResolveName) { WideString wsName; if (CXFA_Node* pXFANode = pNode->AsNode()) { std::optional ret = pXFANode->JSObject()->TryAttribute(XFA_Attribute::Name, false); if (ret.has_value()) wsName = ret.value(); } if (wsName.IsEmpty()) wsName = L"#" + WideString::FromASCII(pNode->GetClassName()); wsSomExpression = wsName + wsSomExpression; dwFlags = XFA_ResolveFlag::kSiblings; } else { dwFlags = (bsSomExp == "*") ? Mask{XFA_ResolveFlag::kChildren} : Mask{XFA_ResolveFlag::kChildren, XFA_ResolveFlag::kAttributes, XFA_ResolveFlag::kProperties}; } } } else { pNode = CFXJSE_Engine::ToObject(pIsolate, pRefValue); dwFlags = XFA_ResolveFlag::kAnyChild; } return pScriptContext->ResolveObjects(pNode, wsSomExpression.AsStringView(), dwFlags); } v8::LocalVector ParseResolveResult( CFXJSE_HostObject* pHostObject, const CFXJSE_Engine::ResolveResult& resolveNodeRS, v8::Local pParentValue, bool* bAttribute) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pHostObject); v8::Isolate* pIsolate = pContext->GetIsolate(); v8::LocalVector resultValues(pIsolate); if (resolveNodeRS.type == CFXJSE_Engine::ResolveResult::Type::kNodes) { *bAttribute = false; CFXJSE_Engine* pScriptContext = pContext->GetDocument()->GetScriptContext(); for (auto& pObject : resolveNodeRS.objects) { resultValues.push_back( pScriptContext->GetOrCreateJSBindingFromMap(pObject.Get())); } return resultValues; } *bAttribute = true; if (resolveNodeRS.script_attribute.callback && resolveNodeRS.script_attribute.eValueType == XFA_ScriptType::Object) { for (auto& pObject : resolveNodeRS.objects) { v8::Local pValue; CJX_Object* jsObject = pObject->JSObject(); (*resolveNodeRS.script_attribute.callback)( pIsolate, jsObject, &pValue, false, resolveNodeRS.script_attribute.attribute); resultValues.push_back(pValue); *bAttribute = false; } } if (*bAttribute && fxv8::IsObject(pParentValue)) resultValues.push_back(pParentValue); return resultValues; } // Returns 0 if the provided `arg` is an invalid payment period count. int GetValidatedPaymentPeriods(v8::Isolate* isolate, v8::Local arg) { double periods = ValueToDouble(isolate, arg); if (periods < 1 || periods > static_cast(std::numeric_limits::max())) { return 0; } return static_cast(periods); } } // namespace const FXJSE_CLASS_DESCRIPTOR kFormCalcDescriptor = { kClassTag, // tag "XFA_FormCalcClass", // name kFormCalcFunctions, // methods nullptr, // dynamic prop type nullptr, // dynamic prop getter nullptr, // dynamic prop setter nullptr, // dynamic prop method call }; // static void CFXJSE_FormCalcContext::Abs( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Abs"); return; } if (ValueIsNull(info.GetIsolate(), info[0])) { info.GetReturnValue().SetNull(); return; } double dValue = ValueToDouble(info.GetIsolate(), info[0]); if (dValue < 0) dValue = -dValue; info.GetReturnValue().Set(dValue); } // static void CFXJSE_FormCalcContext::Avg( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { uint32_t uCount = 0; double dSum = 0.0; auto fn = [&uCount, &dSum](v8::Isolate* pIsolate, v8::Local pValue) { dSum += ValueToDouble(pIsolate, pValue); uCount++; }; if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/false)) return; if (uCount == 0) { info.GetReturnValue().SetNull(); return; } info.GetReturnValue().Set(dSum / uCount); } // static void CFXJSE_FormCalcContext::Ceil( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ceil"); return; } v8::Local argValue = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argValue)) { info.GetReturnValue().SetNull(); return; } info.GetReturnValue().Set(ceil(ValueToFloat(info.GetIsolate(), argValue))); } // static void CFXJSE_FormCalcContext::Count( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { uint32_t iCount = 0; auto fn = [&iCount](v8::Isolate* pIsolate, v8::Local pvalue) { ++iCount; }; if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true)) return; info.GetReturnValue().Set(iCount); } // static void CFXJSE_FormCalcContext::Floor( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Floor"); return; } v8::Local argValue = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argValue)) { info.GetReturnValue().SetNull(); return; } info.GetReturnValue().Set(floor(ValueToFloat(info.GetIsolate(), argValue))); } // static void CFXJSE_FormCalcContext::Max( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { uint32_t uCount = 0; double dMaxValue = 0.0; auto fn = [&uCount, &dMaxValue](v8::Isolate* pIsolate, v8::Local pValue) { ++uCount; double dValue = ValueToDouble(pIsolate, pValue); dMaxValue = uCount == 1 ? dValue : std::max(dMaxValue, dValue); }; if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true)) return; if (uCount == 0) { info.GetReturnValue().SetNull(); return; } info.GetReturnValue().Set(dMaxValue); } // static void CFXJSE_FormCalcContext::Min( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { uint32_t uCount = 0; double dMinValue = 0.0; auto fn = [&uCount, &dMinValue](v8::Isolate* pIsolate, v8::Local pValue) { ++uCount; double dValue = ValueToDouble(pIsolate, pValue); dMinValue = uCount == 1 ? dValue : std::min(dMinValue, dValue); }; if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true)) return; if (uCount == 0) { info.GetReturnValue().SetNull(); return; } info.GetReturnValue().Set(dMinValue); } // static void CFXJSE_FormCalcContext::Mod( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 2) { pContext->ThrowParamCountMismatchException("Mod"); return; } if (fxv8::IsNull(info[0]) || fxv8::IsNull(info[1])) { info.GetReturnValue().SetNull(); return; } std::optional maybe_dividend = ExtractDouble(info.GetIsolate(), info[0]); std::optional maybe_divisor = ExtractDouble(info.GetIsolate(), info[1]); if (!maybe_dividend.has_value() || !maybe_divisor.has_value()) { pContext->ThrowArgumentMismatchException(); return; } double dividend = maybe_dividend.value(); double divisor = maybe_divisor.value(); if (divisor == 0.0) { pContext->ThrowDivideByZeroException(); return; } info.GetReturnValue().Set(dividend - divisor * static_cast(dividend / divisor)); } // static void CFXJSE_FormCalcContext::Round( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); int32_t argc = info.Length(); if (argc < 1 || argc > 2) { pContext->ThrowParamCountMismatchException("Round"); return; } if (fxv8::IsNull(info[0])) { info.GetReturnValue().SetNull(); return; } std::optional maybe_value = ExtractDouble(info.GetIsolate(), info[0]); if (!maybe_value.has_value()) { pContext->ThrowArgumentMismatchException(); return; } double dValue = maybe_value.value(); uint8_t uPrecision = 0; if (argc > 1) { if (fxv8::IsNull(info[1])) { info.GetReturnValue().SetNull(); return; } std::optional maybe_precision = ExtractDouble(info.GetIsolate(), info[1]); if (!maybe_precision.has_value()) { pContext->ThrowArgumentMismatchException(); return; } double dPrecision = maybe_precision.value(); uPrecision = static_cast(std::clamp(dPrecision, 0.0, 12.0)); } CFGAS_Decimal decimalValue(static_cast(dValue), uPrecision); info.GetReturnValue().Set(decimalValue.ToDouble()); } // static void CFXJSE_FormCalcContext::Sum( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { uint32_t uCount = 0; double dSum = 0.0; auto fn = [&uCount, &dSum](v8::Isolate* pIsolate, v8::Local pValue) { ++uCount; dSum += ValueToDouble(pIsolate, pValue); }; if (!ToFormCalcContext(pThis)->ApplyToExpansion(fn, info, /*bStrict=*/true)) return; if (uCount == 0) { info.GetReturnValue().SetNull(); return; } info.GetReturnValue().Set(dSum); } // static void CFXJSE_FormCalcContext::Date( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 0) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date"); return; } time_t currentTime; FXSYS_time(¤tTime); struct tm* pTmStruct = gmtime(¤tTime); info.GetReturnValue().Set(DateString2Num( ByteString::Format("%d%02d%02d", pTmStruct->tm_year + 1900, pTmStruct->tm_mon + 1, pTmStruct->tm_mday) .AsStringView())); } // static void CFXJSE_FormCalcContext::Date2Num( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num"); return; } v8::Local dateValue = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), dateValue)) { info.GetReturnValue().SetNull(); return; } ByteString bsDate = ValueToUTF8String(info.GetIsolate(), dateValue); ByteString bsFormat; if (argc > 1) { v8::Local formatValue = GetSimpleValue(info, 1); if (ValueIsNull(info.GetIsolate(), formatValue)) { info.GetReturnValue().SetNull(); return; } bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); } ByteString bsLocale; if (argc > 2) { v8::Local localeValue = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), localeValue)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); } ByteString bsIsoDate = Local2IsoDate(pThis, bsDate.AsStringView(), bsFormat.AsStringView(), bsLocale.AsStringView()); info.GetReturnValue().Set(DateString2Num(bsIsoDate.AsStringView())); } // static void CFXJSE_FormCalcContext::DateFmt( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc > 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Date2Num"); return; } int32_t iStyle = 0; if (argc > 0) { v8::Local infotyle = GetSimpleValue(info, 0); if (fxv8::IsNull(infotyle)) { info.GetReturnValue().SetNull(); return; } iStyle = static_cast(ValueToFloat(info.GetIsolate(), infotyle)); if (iStyle < 0 || iStyle > 4) iStyle = 0; } ByteString bsLocale; if (argc > 1) { v8::Local argLocale = GetSimpleValue(info, 1); if (fxv8::IsNull(argLocale)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale); } ByteString bsFormat = GetStandardDateFormat(pThis, iStyle, bsLocale.AsStringView()); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView())); } // static void CFXJSE_FormCalcContext::IsoDate2Num( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("IsoDate2Num"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (fxv8::IsNull(argOne)) { info.GetReturnValue().SetNull(); return; } ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); info.GetReturnValue().Set(DateString2Num(bsArg.AsStringView())); } // static void CFXJSE_FormCalcContext::IsoTime2Num( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 1) { pContext->ThrowParamCountMismatchException("IsoTime2Num"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argOne)) { info.GetReturnValue().SetNull(); return; } CXFA_Document* pDoc = pContext->GetDocument(); CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); auto pos = bsArg.Find('T', 0); if (!pos.has_value() || pos.value() == bsArg.GetLength() - 1) { info.GetReturnValue().Set(0); return; } bsArg = bsArg.Last(bsArg.GetLength() - (pos.value() + 1)); CXFA_LocaleValue timeValue(CXFA_LocaleValue::ValueType::kTime, WideString::FromUTF8(bsArg.AsStringView()), pMgr); if (!timeValue.IsValid()) { info.GetReturnValue().Set(0); return; } CFX_DateTime uniTime = timeValue.GetTime(); int32_t hour = uniTime.GetHour(); int32_t min = uniTime.GetMinute(); int32_t second = uniTime.GetSecond(); int32_t milSecond = uniTime.GetMillisecond(); // TODO(dsinclair): See if there is other time conversion code in pdfium and // consolidate. int32_t mins = hour * 60 + min; mins -= pMgr->GetDefLocale()->GetTimeZoneInMinutes(); while (mins > 1440) mins -= 1440; while (mins < 0) mins += 1440; hour = mins / 60; min = mins % 60; info.GetReturnValue().Set(hour * 3600000 + min * 60000 + second * 1000 + milSecond + 1); } // static void CFXJSE_FormCalcContext::LocalDateFmt( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc > 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalDateFmt"); return; } int32_t iStyle = 0; if (argc > 0) { v8::Local infotyle = GetSimpleValue(info, 0); if (fxv8::IsNull(infotyle)) { info.GetReturnValue().SetNull(); return; } iStyle = static_cast(ValueToFloat(info.GetIsolate(), infotyle)); if (iStyle > 4 || iStyle < 0) iStyle = 0; } ByteString bsLocale; if (argc > 1) { v8::Local argLocale = GetSimpleValue(info, 1); if (fxv8::IsNull(argLocale)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale); } ByteString bsFormat = GetLocalDateFormat(pThis, iStyle, bsLocale.AsStringView(), false); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView())); } // static void CFXJSE_FormCalcContext::LocalTimeFmt( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc > 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("LocalTimeFmt"); return; } int32_t iStyle = 0; if (argc > 0) { v8::Local infotyle = GetSimpleValue(info, 0); if (fxv8::IsNull(infotyle)) { info.GetReturnValue().SetNull(); return; } iStyle = static_cast(ValueToFloat(info.GetIsolate(), infotyle)); if (iStyle > 4 || iStyle < 0) iStyle = 0; } ByteString bsLocale; if (argc > 1) { v8::Local argLocale = GetSimpleValue(info, 1); if (fxv8::IsNull(argLocale)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale); } ByteString bsFormat = GetLocalTimeFormat(pThis, iStyle, bsLocale.AsStringView(), false); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView())); } // static void CFXJSE_FormCalcContext::Num2Date( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Date"); return; } v8::Local dateValue = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), dateValue)) { info.GetReturnValue().SetNull(); return; } int32_t dDate = static_cast(ValueToFloat(info.GetIsolate(), dateValue)); if (dDate < 1) { info.GetReturnValue().SetNull(); return; } ByteString bsFormat; if (argc > 1) { v8::Local formatValue = GetSimpleValue(info, 1); if (ValueIsNull(info.GetIsolate(), formatValue)) { info.GetReturnValue().SetNull(); return; } bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); } ByteString bsLocale; if (argc > 2) { v8::Local localeValue = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), localeValue)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); } int32_t iYear = 1900; int32_t iMonth = 1; int32_t iDay = 1; int32_t i = 0; while (dDate > 0) { if (iMonth == 2) { if ((!((iYear + i) % 4) && ((iYear + i) % 100)) || !((iYear + i) % 400)) { if (dDate > 29) { ++iMonth; if (iMonth > 12) { iMonth = 1; ++i; } iDay = 1; dDate -= 29; } else { iDay += static_cast(dDate) - 1; dDate = 0; } } else { if (dDate > 28) { ++iMonth; if (iMonth > 12) { iMonth = 1; ++i; } iDay = 1; dDate -= 28; } else { iDay += static_cast(dDate) - 1; dDate = 0; } } } else if (iMonth < 8) { if ((iMonth % 2 == 0)) { if (dDate > 30) { ++iMonth; if (iMonth > 12) { iMonth = 1; ++i; } iDay = 1; dDate -= 30; } else { iDay += static_cast(dDate) - 1; dDate = 0; } } else { if (dDate > 31) { ++iMonth; if (iMonth > 12) { iMonth = 1; ++i; } iDay = 1; dDate -= 31; } else { iDay += static_cast(dDate) - 1; dDate = 0; } } } else { if (iMonth % 2 != 0) { if (dDate > 30) { ++iMonth; if (iMonth > 12) { iMonth = 1; ++i; } iDay = 1; dDate -= 30; } else { iDay += static_cast(dDate) - 1; dDate = 0; } } else { if (dDate > 31) { ++iMonth; if (iMonth > 12) { iMonth = 1; ++i; } iDay = 1; dDate -= 31; } else { iDay += static_cast(dDate) - 1; dDate = 0; } } } } ByteString bsLocalDate = IsoDate2Local( pThis, ByteString::Format("%d%02d%02d", iYear + i, iMonth, iDay).AsStringView(), bsFormat.AsStringView(), bsLocale.AsStringView()); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsLocalDate.AsStringView())); } // static void CFXJSE_FormCalcContext::Num2GMTime( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2GMTime"); return; } v8::Local timeValue = GetSimpleValue(info, 0); if (fxv8::IsNull(timeValue)) { info.GetReturnValue().SetNull(); return; } int32_t iTime = static_cast(ValueToFloat(info.GetIsolate(), timeValue)); if (abs(iTime) < 1.0) { info.GetReturnValue().SetNull(); return; } ByteString bsFormat; if (argc > 1) { v8::Local formatValue = GetSimpleValue(info, 1); if (fxv8::IsNull(formatValue)) { info.GetReturnValue().SetNull(); return; } bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); } ByteString bsLocale; if (argc > 2) { v8::Local localeValue = GetSimpleValue(info, 2); if (fxv8::IsNull(localeValue)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); } ByteString bsGMTTime = Num2AllTime(pThis, iTime, bsFormat.AsStringView(), bsLocale.AsStringView(), true); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsGMTTime.AsStringView())); } // static void CFXJSE_FormCalcContext::Num2Time( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Num2Time"); return; } v8::Local timeValue = GetSimpleValue(info, 0); if (fxv8::IsNull(timeValue)) { info.GetReturnValue().SetNull(); return; } float fTime = ValueToFloat(info.GetIsolate(), timeValue); if (fabs(fTime) < 1.0) { info.GetReturnValue().SetNull(); return; } ByteString bsFormat; if (argc > 1) { v8::Local formatValue = GetSimpleValue(info, 1); if (fxv8::IsNull(formatValue)) { info.GetReturnValue().SetNull(); return; } bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); } ByteString bsLocale; if (argc > 2) { v8::Local localeValue = GetSimpleValue(info, 2); if (fxv8::IsNull(localeValue)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); } ByteString bsLocalTime = Num2AllTime(pThis, static_cast(fTime), bsFormat.AsStringView(), bsLocale.AsStringView(), false); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsLocalTime.AsStringView())); } // static void CFXJSE_FormCalcContext::Time( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 0) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time"); return; } time_t now; FXSYS_time(&now); struct tm* pGmt = gmtime(&now); info.GetReturnValue().Set( (pGmt->tm_hour * 3600 + pGmt->tm_min * 60 + pGmt->tm_sec) * 1000); } // static void CFXJSE_FormCalcContext::Time2Num( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Time2Num"); return; } ByteString bsTime; v8::Local timeValue = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), timeValue)) { info.GetReturnValue().SetNull(); return; } bsTime = ValueToUTF8String(info.GetIsolate(), timeValue); ByteString bsFormat; if (argc > 1) { v8::Local formatValue = GetSimpleValue(info, 1); if (ValueIsNull(info.GetIsolate(), formatValue)) { info.GetReturnValue().SetNull(); return; } bsFormat = ValueToUTF8String(info.GetIsolate(), formatValue); } ByteString bsLocale; if (argc > 2) { v8::Local localeValue = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), localeValue)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); } CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument(); CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); GCedLocaleIface* pLocale = nullptr; if (!bsLocale.IsEmpty()) { pLocale = pMgr->GetLocaleByName(WideString::FromUTF8(bsLocale.AsStringView())); } if (!pLocale) { CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject()); pLocale = pThisNode->GetLocale(); } WideString wsFormat; if (bsFormat.IsEmpty()) { wsFormat = pLocale->GetTimePattern(LocaleIface::DateTimeSubcategory::kDefault); } else { wsFormat = WideString::FromUTF8(bsFormat.AsStringView()); } wsFormat = L"time{" + wsFormat + L"}"; CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kTime, WideString::FromUTF8(bsTime.AsStringView()), wsFormat, pLocale, pMgr); if (!localeValue.IsValid()) { info.GetReturnValue().Set(0); return; } CFX_DateTime uniTime = localeValue.GetTime(); int32_t hour = uniTime.GetHour(); int32_t minute = uniTime.GetMinute(); const int32_t second = uniTime.GetSecond(); const int32_t millisecond = uniTime.GetMillisecond(); constexpr int kMinutesInDay = 24 * 60; int32_t minutes_with_tz = hour * 60 + minute - CXFA_TimeZoneProvider().GetTimeZoneInMinutes(); minutes_with_tz %= kMinutesInDay; if (minutes_with_tz < 0) minutes_with_tz += kMinutesInDay; hour = minutes_with_tz / 60; minute = minutes_with_tz % 60; info.GetReturnValue().Set(hour * 3600000 + minute * 60000 + second * 1000 + millisecond + 1); } // static void CFXJSE_FormCalcContext::TimeFmt( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc > 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("TimeFmt"); return; } int32_t iStyle = 0; if (argc > 0) { v8::Local infotyle = GetSimpleValue(info, 0); if (fxv8::IsNull(infotyle)) { info.GetReturnValue().SetNull(); return; } iStyle = static_cast(ValueToFloat(info.GetIsolate(), infotyle)); if (iStyle > 4 || iStyle < 0) iStyle = 0; } ByteString bsLocale; if (argc > 1) { v8::Local argLocale = GetSimpleValue(info, 1); if (fxv8::IsNull(argLocale)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), argLocale); } ByteString bsFormat = GetStandardTimeFormat(pThis, iStyle, bsLocale.AsStringView()); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsFormat.AsStringView())); } // static ByteString CFXJSE_FormCalcContext::Local2IsoDate(CFXJSE_HostObject* pThis, ByteStringView bsDate, ByteStringView bsFormat, ByteStringView bsLocale) { CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument(); if (!pDoc) return ByteString(); CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); if (!pLocale) return ByteString(); WideString wsFormat = FormatFromString(pLocale, bsFormat); CFX_DateTime dt = CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate, WideString::FromUTF8(bsDate), wsFormat, pLocale, pMgr) .GetDate(); return ByteString::Format("%4d-%02d-%02d", dt.GetYear(), dt.GetMonth(), dt.GetDay()); } // static ByteString CFXJSE_FormCalcContext::IsoDate2Local(CFXJSE_HostObject* pThis, ByteStringView bsDate, ByteStringView bsFormat, ByteStringView bsLocale) { CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument(); if (!pDoc) return ByteString(); CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); if (!pLocale) return ByteString(); WideString wsFormat = FormatFromString(pLocale, bsFormat); WideString wsRet; CXFA_LocaleValue(CXFA_LocaleValue::ValueType::kDate, WideString::FromUTF8(bsDate), pMgr) .FormatPatterns(wsRet, wsFormat, pLocale, XFA_ValuePicture::kDisplay); return wsRet.ToUTF8(); } // static ByteString CFXJSE_FormCalcContext::IsoTime2Local(CFXJSE_HostObject* pThis, ByteStringView bsTime, ByteStringView bsFormat, ByteStringView bsLocale) { CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument(); if (!pDoc) return ByteString(); CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); GCedLocaleIface* pLocale = LocaleFromString(pDoc, pMgr, bsLocale); if (!pLocale) return ByteString(); WideString wsFormat = { L"time{", FormatFromString(pLocale, bsFormat).AsStringView(), L"}"}; CXFA_LocaleValue widgetValue(CXFA_LocaleValue::ValueType::kTime, WideString::FromUTF8(bsTime), pMgr); WideString wsRet; widgetValue.FormatPatterns(wsRet, wsFormat, pLocale, XFA_ValuePicture::kDisplay); return wsRet.ToUTF8(); } // static ByteString CFXJSE_FormCalcContext::GetLocalDateFormat(CFXJSE_HostObject* pThis, int32_t iStyle, ByteStringView bsLocale, bool bStandard) { CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument(); if (!pDoc) return ByteString(); return GetLocalDateTimeFormat(pDoc, iStyle, bsLocale, bStandard, /*bIsDate=*/true); } // static ByteString CFXJSE_FormCalcContext::GetLocalTimeFormat(CFXJSE_HostObject* pThis, int32_t iStyle, ByteStringView bsLocale, bool bStandard) { CXFA_Document* pDoc = ToFormCalcContext(pThis)->GetDocument(); if (!pDoc) return ByteString(); return GetLocalDateTimeFormat(pDoc, iStyle, bsLocale, bStandard, /*bIsDate=*/false); } // static ByteString CFXJSE_FormCalcContext::GetStandardDateFormat( CFXJSE_HostObject* pThis, int32_t iStyle, ByteStringView bsLocale) { return GetLocalDateFormat(pThis, iStyle, bsLocale, true); } // static ByteString CFXJSE_FormCalcContext::GetStandardTimeFormat( CFXJSE_HostObject* pThis, int32_t iStyle, ByteStringView bsLocale) { return GetLocalTimeFormat(pThis, iStyle, bsLocale, true); } // static ByteString CFXJSE_FormCalcContext::Num2AllTime(CFXJSE_HostObject* pThis, int32_t iTime, ByteStringView bsFormat, ByteStringView bsLocale, bool bGM) { int32_t iHour = 0; int32_t iMin = 0; int32_t iSec = 0; iHour = static_cast(iTime) / 3600000; iMin = (static_cast(iTime) - iHour * 3600000) / 60000; iSec = (static_cast(iTime) - iHour * 3600000 - iMin * 60000) / 1000; if (!bGM) { int32_t iZoneHour; int32_t iZoneMin; int32_t iZoneSec; GetLocalTimeZone(&iZoneHour, &iZoneMin, &iZoneSec); iHour += iZoneHour; iMin += iZoneMin; iSec += iZoneSec; } return IsoTime2Local( pThis, ByteString::Format("%02d:%02d:%02d", iHour, iMin, iSec).AsStringView(), bsFormat, bsLocale); } // static void CFXJSE_FormCalcContext::Apr( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 3) { pContext->ThrowParamCountMismatchException("Apr"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); v8::Local argThree = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo) || ValueIsNull(info.GetIsolate(), argThree)) { info.GetReturnValue().SetNull(); return; } double nPrincipal = ValueToDouble(info.GetIsolate(), argOne); double nPayment = ValueToDouble(info.GetIsolate(), argTwo); int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); if (nPrincipal <= 0 || nPayment <= 0 || nPeriods == 0) { pContext->ThrowArgumentMismatchException(); return; } double r = 2 * (nPeriods * nPayment - nPrincipal) / (nPeriods * nPrincipal); double nTemp = 1; for (int32_t i = 0; i < nPeriods; ++i) nTemp *= (1 + r); double nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal; while (fabs(nRet) > kFinancialPrecision) { double nDerivative = ((nTemp + r * nPeriods * (nTemp / (1 + r))) * (nTemp - 1) - (r * nTemp * nPeriods * (nTemp / (1 + r)))) / ((nTemp - 1) * (nTemp - 1)); if (nDerivative == 0) { info.GetReturnValue().SetNull(); return; } r = r - nRet / nDerivative; nTemp = 1; for (int32_t i = 0; i < nPeriods; ++i) { nTemp *= (1 + r); } nRet = r * nTemp / (nTemp - 1) - nPayment / nPrincipal; } info.GetReturnValue().Set(r * 12); } // static void CFXJSE_FormCalcContext::CTerm( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 3) { pContext->ThrowParamCountMismatchException("CTerm"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); v8::Local argThree = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo) || ValueIsNull(info.GetIsolate(), argThree)) { info.GetReturnValue().SetNull(); return; } float nRate = ValueToFloat(info.GetIsolate(), argOne); float nFutureValue = ValueToFloat(info.GetIsolate(), argTwo); float nInitAmount = ValueToFloat(info.GetIsolate(), argThree); if ((nRate <= 0) || (nFutureValue <= 0) || (nInitAmount <= 0)) { pContext->ThrowArgumentMismatchException(); return; } info.GetReturnValue().Set(log((float)(nFutureValue / nInitAmount)) / log((float)(1 + nRate))); } // static void CFXJSE_FormCalcContext::FV( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 3) { pContext->ThrowParamCountMismatchException("FV"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); v8::Local argThree = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo) || ValueIsNull(info.GetIsolate(), argThree)) { info.GetReturnValue().SetNull(); return; } double nAmount = ValueToDouble(info.GetIsolate(), argOne); double nRate = ValueToDouble(info.GetIsolate(), argTwo); int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); if (nAmount <= 0 || nRate < 0 || nPeriods == 0) { pContext->ThrowArgumentMismatchException(); return; } double dResult = 0; if (nRate) { double nTemp = 1; for (int i = 0; i < nPeriods; ++i) { nTemp *= 1 + nRate; } dResult = nAmount * (nTemp - 1) / nRate; } else { dResult = nAmount * nPeriods; } info.GetReturnValue().Set(dResult); } // static void CFXJSE_FormCalcContext::IPmt( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 5) { pContext->ThrowParamCountMismatchException("IPmt"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); v8::Local argThree = GetSimpleValue(info, 2); v8::Local argFour = GetSimpleValue(info, 3); v8::Local argFive = GetSimpleValue(info, 4); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo) || ValueIsNull(info.GetIsolate(), argThree) || ValueIsNull(info.GetIsolate(), argFour) || ValueIsNull(info.GetIsolate(), argFive)) { info.GetReturnValue().SetNull(); return; } float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne); float nRate = ValueToFloat(info.GetIsolate(), argTwo); float nPayment = ValueToFloat(info.GetIsolate(), argThree); float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour); float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive); if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) || (nFirstMonth < 0) || (nNumberOfMonths < 0)) { pContext->ThrowArgumentMismatchException(); return; } float nRateOfMonth = nRate / 12; int32_t iNums = static_cast( (log10((float)(nPayment / nPrincipalAmount)) - log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) / log10((float)(1 + nRateOfMonth))); int32_t iEnd = std::min(static_cast(nFirstMonth + nNumberOfMonths - 1), iNums); if (nPayment < nPrincipalAmount * nRateOfMonth) { info.GetReturnValue().Set(0); return; } int32_t i = 0; for (i = 0; i < nFirstMonth - 1; ++i) nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth; float nSum = 0; for (; i < iEnd; ++i) { nSum += nPrincipalAmount * nRateOfMonth; nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth; } info.GetReturnValue().Set(nSum); } // static void CFXJSE_FormCalcContext::NPV( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); int32_t argc = info.Length(); if (argc < 2) { pContext->ThrowParamCountMismatchException("NPV"); return; } v8::Local argValue = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argValue)) { info.GetReturnValue().SetNull(); return; } double nRate = ValueToDouble(info.GetIsolate(), argValue); if (nRate <= 0) { pContext->ThrowArgumentMismatchException(); return; } std::vector data; for (int32_t i = 1; i < argc; i++) { argValue = GetSimpleValue(info, i); if (ValueIsNull(info.GetIsolate(), argValue)) { info.GetReturnValue().SetNull(); return; } data.push_back(ValueToDouble(info.GetIsolate(), argValue)); } double nSum = 0; double nDivisor = 1.0 + nRate; while (!data.empty()) { nSum += data.back(); nSum /= nDivisor; data.pop_back(); } info.GetReturnValue().Set(nSum); } // static void CFXJSE_FormCalcContext::Pmt( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 3) { pContext->ThrowParamCountMismatchException("Pmt"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); v8::Local argThree = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo) || ValueIsNull(info.GetIsolate(), argThree)) { info.GetReturnValue().SetNull(); return; } double nPrincipal = ValueToDouble(info.GetIsolate(), argOne); double nRate = ValueToDouble(info.GetIsolate(), argTwo); int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); if (nPrincipal <= 0 || nRate <= 0 || nPeriods == 0) { pContext->ThrowArgumentMismatchException(); return; } double nSum = pow(1.0 + nRate, nPeriods); info.GetReturnValue().Set((nPrincipal * nRate * nSum) / (nSum - 1)); } // static void CFXJSE_FormCalcContext::PPmt( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 5) { pContext->ThrowParamCountMismatchException("PPmt"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); v8::Local argThree = GetSimpleValue(info, 2); v8::Local argFour = GetSimpleValue(info, 3); v8::Local argFive = GetSimpleValue(info, 4); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo) || ValueIsNull(info.GetIsolate(), argThree) || ValueIsNull(info.GetIsolate(), argFour) || ValueIsNull(info.GetIsolate(), argFive)) { info.GetReturnValue().SetNull(); return; } float nPrincipalAmount = ValueToFloat(info.GetIsolate(), argOne); float nRate = ValueToFloat(info.GetIsolate(), argTwo); float nPayment = ValueToFloat(info.GetIsolate(), argThree); float nFirstMonth = ValueToFloat(info.GetIsolate(), argFour); float nNumberOfMonths = ValueToFloat(info.GetIsolate(), argFive); if ((nPrincipalAmount <= 0) || (nRate <= 0) || (nPayment <= 0) || (nFirstMonth < 0) || (nNumberOfMonths < 0)) { pContext->ThrowArgumentMismatchException(); return; } float nRateOfMonth = nRate / 12; int32_t iNums = static_cast( (log10((float)(nPayment / nPrincipalAmount)) - log10((float)(nPayment / nPrincipalAmount - nRateOfMonth))) / log10((float)(1 + nRateOfMonth))); int32_t iEnd = std::min(static_cast(nFirstMonth + nNumberOfMonths - 1), iNums); if (nPayment < nPrincipalAmount * nRateOfMonth) { pContext->ThrowArgumentMismatchException(); return; } int32_t i = 0; for (i = 0; i < nFirstMonth - 1; ++i) nPrincipalAmount -= nPayment - nPrincipalAmount * nRateOfMonth; float nTemp = 0; float nSum = 0; for (; i < iEnd; ++i) { nTemp = nPayment - nPrincipalAmount * nRateOfMonth; nSum += nTemp; nPrincipalAmount -= nTemp; } info.GetReturnValue().Set(nSum); } // static void CFXJSE_FormCalcContext::PV( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 3) { pContext->ThrowParamCountMismatchException("PV"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); v8::Local argThree = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo) || ValueIsNull(info.GetIsolate(), argThree)) { info.GetReturnValue().SetNull(); return; } double nAmount = ValueToDouble(info.GetIsolate(), argOne); double nRate = ValueToDouble(info.GetIsolate(), argTwo); int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); if (nAmount <= 0 || nRate < 0 || nPeriods == 0) { pContext->ThrowArgumentMismatchException(); return; } double nTemp = 1 / pow(1.0 + nRate, nPeriods); info.GetReturnValue().Set(nAmount * ((1.0 - nTemp) / nRate)); } // static void CFXJSE_FormCalcContext::Rate( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 3) { pContext->ThrowParamCountMismatchException("Rate"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); v8::Local argThree = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo) || ValueIsNull(info.GetIsolate(), argThree)) { info.GetReturnValue().SetNull(); return; } float nFuture = ValueToFloat(info.GetIsolate(), argOne); float nPresent = ValueToFloat(info.GetIsolate(), argTwo); int nPeriods = GetValidatedPaymentPeriods(info.GetIsolate(), argThree); if (nFuture <= 0 || nPresent < 0 || nPeriods == 0) { pContext->ThrowArgumentMismatchException(); return; } info.GetReturnValue().Set(powf(nFuture / nPresent, 1.0f / nPeriods) - 1.0f); } // static void CFXJSE_FormCalcContext::Term( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 3) { pContext->ThrowParamCountMismatchException("Term"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); v8::Local argThree = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo) || ValueIsNull(info.GetIsolate(), argThree)) { info.GetReturnValue().SetNull(); return; } float nMount = ValueToFloat(info.GetIsolate(), argOne); float nRate = ValueToFloat(info.GetIsolate(), argTwo); float nFuture = ValueToFloat(info.GetIsolate(), argThree); if ((nMount <= 0) || (nRate <= 0) || (nFuture <= 0)) { pContext->ThrowArgumentMismatchException(); return; } info.GetReturnValue().Set(log((float)(nFuture / nMount * nRate) + 1) / log((float)(1 + nRate))); } // static void CFXJSE_FormCalcContext::Choose( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); int32_t argc = info.Length(); if (argc < 2) { pContext->ThrowParamCountMismatchException("Choose"); return; } if (ValueIsNull(info.GetIsolate(), info[0])) { info.GetReturnValue().SetNull(); return; } int32_t iIndex = static_cast(ValueToFloat(info.GetIsolate(), info[0])); if (iIndex < 1) { info.GetReturnValue().SetEmptyString(); return; } bool bFound = false; bool bStopCounterFlags = false; int32_t iArgIndex = 1; int32_t iValueIndex = 0; while (!bFound && !bStopCounterFlags && (iArgIndex < argc)) { v8::Local argIndexValue = info[iArgIndex]; if (fxv8::IsArray(argIndexValue)) { v8::Local arr = argIndexValue.As(); uint32_t iLength = fxv8::GetArrayLengthHelper(arr); if (iLength > 3) bStopCounterFlags = true; iValueIndex += (iLength - 2); if (iValueIndex >= iIndex) { v8::Local propertyValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1); v8::Local jsValue = fxv8::ReentrantGetArrayElementHelper( info.GetIsolate(), arr, (iLength - 1) - (iValueIndex - iIndex)); v8::Local newPropertyValue; if (fxv8::IsObject(jsValue)) { v8::Local jsObjectValue = jsValue.As(); if (fxv8::IsNull(propertyValue)) { newPropertyValue = GetObjectDefaultValue(info.GetIsolate(), jsObjectValue); } else { ByteString bsName = fxv8::ReentrantToByteStringHelper( info.GetIsolate(), propertyValue); newPropertyValue = fxv8::ReentrantGetObjectPropertyHelper( info.GetIsolate(), jsObjectValue, bsName.AsStringView()); } } ByteString bsChosen = ValueToUTF8String(info.GetIsolate(), newPropertyValue); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView())); bFound = true; } } else { iValueIndex++; if (iValueIndex == iIndex) { ByteString bsChosen = ValueToUTF8String(info.GetIsolate(), argIndexValue); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsChosen.AsStringView())); bFound = true; } } iArgIndex++; } if (!bFound) info.GetReturnValue().SetEmptyString(); } // static void CFXJSE_FormCalcContext::Exists( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Exists"); return; } info.GetReturnValue().Set(fxv8::IsObject(info[0])); } // static void CFXJSE_FormCalcContext::HasValue( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("HasValue"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (!fxv8::IsString(argOne)) { info.GetReturnValue().Set( static_cast(fxv8::IsNumber(argOne) || fxv8::IsBoolean(argOne))); return; } ByteString bsValue = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argOne); bsValue.TrimWhitespaceFront(); info.GetReturnValue().Set(static_cast(!bsValue.IsEmpty())); } // static void CFXJSE_FormCalcContext::Oneof( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() < 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Oneof"); return; } v8::Local argOne = GetSimpleValue(info, 0); for (const auto& value : UnfoldArgs(info)) { if (SimpleValueCompare(info.GetIsolate(), argOne, value)) { info.GetReturnValue().Set(1); return; } } info.GetReturnValue().Set(0); } // static void CFXJSE_FormCalcContext::Within( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Within"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (fxv8::IsNull(argOne)) { info.GetReturnValue().SetUndefined(); return; } v8::Local argLow = GetSimpleValue(info, 1); v8::Local argHigh = GetSimpleValue(info, 2); if (fxv8::IsNumber(argOne)) { float oneNumber = ValueToFloat(info.GetIsolate(), argOne); float lowNumber = ValueToFloat(info.GetIsolate(), argLow); float heightNumber = ValueToFloat(info.GetIsolate(), argHigh); info.GetReturnValue().Set(static_cast((oneNumber >= lowNumber) && (oneNumber <= heightNumber))); return; } ByteString bsOne = ValueToUTF8String(info.GetIsolate(), argOne); ByteString bsLow = ValueToUTF8String(info.GetIsolate(), argLow); ByteString bsHeight = ValueToUTF8String(info.GetIsolate(), argHigh); info.GetReturnValue().Set( static_cast((bsOne.Compare(bsLow.AsStringView()) >= 0) && (bsOne.Compare(bsHeight.AsStringView()) <= 0))); } // static void CFXJSE_FormCalcContext::If( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("If"); return; } const bool condition = fxv8::ReentrantToBooleanHelper( info.GetIsolate(), GetSimpleValue(info, 0)); info.GetReturnValue().Set(GetSimpleValue(info, condition ? 1 : 2)); } // static void CFXJSE_FormCalcContext::Eval( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 1) { pContext->ThrowParamCountMismatchException("Eval"); return; } v8::Isolate* pIsolate = pContext->GetIsolate(); v8::Local scriptValue = GetSimpleValue(info, 0); ByteString bsUtf8Script = ValueToUTF8String(info.GetIsolate(), scriptValue); if (bsUtf8Script.IsEmpty()) { info.GetReturnValue().SetNull(); return; } WideString wsCalcScript = WideString::FromUTF8(bsUtf8Script.AsStringView()); std::optional wsJavaScriptBuf = CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(), wsCalcScript.AsStringView()); if (!wsJavaScriptBuf.has_value()) { pContext->ThrowCompilerErrorException(); return; } std::unique_ptr pNewContext = CFXJSE_Context::Create(pIsolate, nullptr, nullptr, nullptr); ByteString bsScript = FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView()); CFXJSE_Context::ExecutionResult result = pNewContext->ExecuteScript( bsScript.AsStringView(), v8::Local()); info.GetReturnValue().Set(result.value->DirectGetValue()); } // static void CFXJSE_FormCalcContext::Ref( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 1) { pContext->ThrowParamCountMismatchException("Ref"); return; } v8::Local argOne = info[0]; if (fxv8::IsBoolean(argOne) || fxv8::IsString(argOne) || fxv8::IsNumber(argOne)) { info.GetReturnValue().Set(argOne); return; } v8::LocalVector values(info.GetIsolate(), 3); int intVal = 3; if (fxv8::IsNull(argOne)) { // TODO(dsinclair): Why is this 4 when the others are all 3? intVal = 4; values[2] = fxv8::NewNullHelper(info.GetIsolate()); } else if (fxv8::IsArray(argOne)) { v8::Local arr = argOne.As(); v8::Local propertyValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1); v8::Local jsObjectValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2); if (!fxv8::IsNull(propertyValue) || fxv8::IsNull(jsObjectValue)) { pContext->ThrowArgumentMismatchException(); return; } values[2] = jsObjectValue; } else if (fxv8::IsObject(argOne)) { values[2] = argOne; } else { pContext->ThrowArgumentMismatchException(); return; } values[0] = fxv8::NewNumberHelper(info.GetIsolate(), intVal); values[1] = fxv8::NewNullHelper(info.GetIsolate()); info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values)); } // static void CFXJSE_FormCalcContext::UnitType( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitType"); return; } v8::Local unitspanValue = GetSimpleValue(info, 0); if (fxv8::IsNull(unitspanValue)) { info.GetReturnValue().SetNull(); return; } ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue); if (bsUnitspan.IsEmpty()) { info.GetReturnValue().SetEmptyString(); return; } enum XFA_FormCalc_VALUETYPE_ParserStatus { VALUETYPE_START, VALUETYPE_HAVEINVALIDCHAR, VALUETYPE_HAVEDIGIT, VALUETYPE_HAVEDIGITWHITE, VALUETYPE_ISCM, VALUETYPE_ISMM, VALUETYPE_ISPT, VALUETYPE_ISMP, VALUETYPE_ISIN, }; bsUnitspan.MakeLower(); WideString wsType = WideString::FromUTF8(bsUnitspan.AsStringView()); const wchar_t* pData = wsType.c_str(); int32_t u = 0; int32_t uLen = wsType.GetLength(); UNSAFE_TODO({ while (IsWhitespace(pData[u])) { u++; } XFA_FormCalc_VALUETYPE_ParserStatus eParserStatus = VALUETYPE_START; wchar_t typeChar; // TODO(dsinclair): Cleanup this parser, figure out what the various checks // are for. while (u < uLen) { typeChar = pData[u]; if (IsWhitespace(typeChar)) { if (eParserStatus != VALUETYPE_HAVEDIGIT && eParserStatus != VALUETYPE_HAVEDIGITWHITE) { eParserStatus = VALUETYPE_ISIN; break; } eParserStatus = VALUETYPE_HAVEDIGITWHITE; } else if (IsPartOfNumberW(typeChar)) { if (eParserStatus == VALUETYPE_HAVEDIGITWHITE) { eParserStatus = VALUETYPE_ISIN; break; } eParserStatus = VALUETYPE_HAVEDIGIT; } else if ((typeChar == 'c' || typeChar == 'p') && (u + 1 < uLen)) { wchar_t nextChar = pData[u + 1]; if ((eParserStatus == VALUETYPE_START || eParserStatus == VALUETYPE_HAVEDIGIT || eParserStatus == VALUETYPE_HAVEDIGITWHITE) && !IsPartOfNumberW(nextChar)) { eParserStatus = (typeChar == 'c') ? VALUETYPE_ISCM : VALUETYPE_ISPT; break; } eParserStatus = VALUETYPE_HAVEINVALIDCHAR; } else if (typeChar == 'm' && (u + 1 < uLen)) { wchar_t nextChar = pData[u + 1]; if ((eParserStatus == VALUETYPE_START || eParserStatus == VALUETYPE_HAVEDIGIT || eParserStatus == VALUETYPE_HAVEDIGITWHITE) && !IsPartOfNumberW(nextChar)) { eParserStatus = VALUETYPE_ISMM; if (nextChar == 'p' || ((u + 5 < uLen) && pData[u + 1] == 'i' && pData[u + 2] == 'l' && pData[u + 3] == 'l' && pData[u + 4] == 'i' && pData[u + 5] == 'p')) { eParserStatus = VALUETYPE_ISMP; } break; } } else { eParserStatus = VALUETYPE_HAVEINVALIDCHAR; } u++; } switch (eParserStatus) { case VALUETYPE_ISCM: info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), "cm")); break; case VALUETYPE_ISMM: info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), "mm")); break; case VALUETYPE_ISPT: info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), "pt")); break; case VALUETYPE_ISMP: info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), "mp")); break; default: info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), "in")); break; } }); } // static void CFXJSE_FormCalcContext::UnitValue( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("UnitValue"); return; } v8::Local unitspanValue = GetSimpleValue(info, 0); if (fxv8::IsNull(unitspanValue)) { info.GetReturnValue().SetNull(); return; } ByteString bsUnitspan = ValueToUTF8String(info.GetIsolate(), unitspanValue); const char* pData = bsUnitspan.c_str(); if (!pData) { info.GetReturnValue().Set(0); return; } UNSAFE_TODO({ size_t u = 0; while (IsWhitespace(pData[u])) { ++u; } while (u < bsUnitspan.GetLength()) { if (!IsPartOfNumber(pData[u])) { break; } ++u; } char* pTemp = nullptr; double dFirstNumber = strtod(pData, &pTemp); while (IsWhitespace(pData[u])) { ++u; } size_t uLen = bsUnitspan.GetLength(); ByteString bsFirstUnit; while (u < uLen) { if (pData[u] == ' ') { break; } bsFirstUnit += pData[u]; ++u; } bsFirstUnit.MakeLower(); ByteString bsUnit; if (argc > 1) { v8::Local unitValue = GetSimpleValue(info, 1); ByteString bsUnitTemp = ValueToUTF8String(info.GetIsolate(), unitValue); const char* pChar = bsUnitTemp.c_str(); size_t uVal = 0; while (IsWhitespace(pChar[uVal])) { ++uVal; } while (uVal < bsUnitTemp.GetLength()) { if (!isdigit(pChar[uVal]) && pChar[uVal] != '.') { break; } ++uVal; } while (IsWhitespace(pChar[uVal])) { ++uVal; } size_t uValLen = bsUnitTemp.GetLength(); while (uVal < uValLen) { if (pChar[uVal] == ' ') { break; } bsUnit += pChar[uVal]; ++uVal; } bsUnit.MakeLower(); } else { bsUnit = bsFirstUnit; } double dResult = 0; if (bsFirstUnit == "in" || bsFirstUnit == "inches") { if (bsUnit == "mm" || bsUnit == "millimeters") { dResult = dFirstNumber * 25.4; } else if (bsUnit == "cm" || bsUnit == "centimeters") { dResult = dFirstNumber * 2.54; } else if (bsUnit == "pt" || bsUnit == "points") { dResult = dFirstNumber / 72; } else if (bsUnit == "mp" || bsUnit == "millipoints") { dResult = dFirstNumber / 72000; } else { dResult = dFirstNumber; } } else if (bsFirstUnit == "mm" || bsFirstUnit == "millimeters") { if (bsUnit == "mm" || bsUnit == "millimeters") { dResult = dFirstNumber; } else if (bsUnit == "cm" || bsUnit == "centimeters") { dResult = dFirstNumber / 10; } else if (bsUnit == "pt" || bsUnit == "points") { dResult = dFirstNumber / 25.4 / 72; } else if (bsUnit == "mp" || bsUnit == "millipoints") { dResult = dFirstNumber / 25.4 / 72000; } else { dResult = dFirstNumber / 25.4; } } else if (bsFirstUnit == "cm" || bsFirstUnit == "centimeters") { if (bsUnit == "mm" || bsUnit == "millimeters") { dResult = dFirstNumber * 10; } else if (bsUnit == "cm" || bsUnit == "centimeters") { dResult = dFirstNumber; } else if (bsUnit == "pt" || bsUnit == "points") { dResult = dFirstNumber / 2.54 / 72; } else if (bsUnit == "mp" || bsUnit == "millipoints") { dResult = dFirstNumber / 2.54 / 72000; } else { dResult = dFirstNumber / 2.54; } } else if (bsFirstUnit == "pt" || bsFirstUnit == "points") { if (bsUnit == "mm" || bsUnit == "millimeters") { dResult = dFirstNumber / 72 * 25.4; } else if (bsUnit == "cm" || bsUnit == "centimeters") { dResult = dFirstNumber / 72 * 2.54; } else if (bsUnit == "pt" || bsUnit == "points") { dResult = dFirstNumber; } else if (bsUnit == "mp" || bsUnit == "millipoints") { dResult = dFirstNumber * 1000; } else { dResult = dFirstNumber / 72; } } else if (bsFirstUnit == "mp" || bsFirstUnit == "millipoints") { if (bsUnit == "mm" || bsUnit == "millimeters") { dResult = dFirstNumber / 72000 * 25.4; } else if (bsUnit == "cm" || bsUnit == "centimeters") { dResult = dFirstNumber / 72000 * 2.54; } else if (bsUnit == "pt" || bsUnit == "points") { dResult = dFirstNumber / 1000; } else if (bsUnit == "mp" || bsUnit == "millipoints") { dResult = dFirstNumber; } else { dResult = dFirstNumber / 72000; } } info.GetReturnValue().Set(dResult); }); } // static void CFXJSE_FormCalcContext::At( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("At"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo)) { info.GetReturnValue().SetNull(); return; } ByteString stringTwo = ValueToUTF8String(info.GetIsolate(), argTwo); if (stringTwo.IsEmpty()) { info.GetReturnValue().Set(1); return; } ByteString stringOne = ValueToUTF8String(info.GetIsolate(), argOne); auto pos = stringOne.Find(stringTwo.AsStringView()); info.GetReturnValue().Set( static_cast(pos.has_value() ? pos.value() + 1 : 0)); } // static void CFXJSE_FormCalcContext::Concat( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Concat"); return; } ByteString bsResult; bool bAllNull = true; for (int32_t i = 0; i < argc; i++) { v8::Local value = GetSimpleValue(info, i); if (ValueIsNull(info.GetIsolate(), value)) continue; bAllNull = false; bsResult += ValueToUTF8String(info.GetIsolate(), value); } if (bAllNull) { info.GetReturnValue().SetNull(); return; } info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView())); } // static void CFXJSE_FormCalcContext::Decode( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Decode"); return; } if (argc == 1) { v8::Local argOne = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argOne)) { info.GetReturnValue().SetNull(); return; } WideString decoded = DecodeURL(WideString::FromUTF8( ValueToUTF8String(info.GetIsolate(), argOne).AsStringView())); auto result = FX_UTF8Encode(decoded.AsStringView()); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo)) { info.GetReturnValue().SetNull(); return; } ByteString bsToDecode = ValueToUTF8String(info.GetIsolate(), argOne); ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo); WideString decoded; WideString wsToDecode = WideString::FromUTF8(bsToDecode.AsStringView()); if (bsIdentify.EqualNoCase("html")) decoded = DecodeHTML(wsToDecode); else if (bsIdentify.EqualNoCase("xml")) decoded = DecodeXML(wsToDecode); else decoded = DecodeURL(wsToDecode); auto result = FX_UTF8Encode(decoded.AsStringView()); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); } // static void CFXJSE_FormCalcContext::Encode( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Encode"); return; } if (argc == 1) { v8::Local argOne = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argOne)) { info.GetReturnValue().SetNull(); return; } WideString encoded = EncodeURL(ValueToUTF8String(info.GetIsolate(), argOne)); auto result = FX_UTF8Encode(encoded.AsStringView()); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); if (ValueIsNull(info.GetIsolate(), argOne) || ValueIsNull(info.GetIsolate(), argTwo)) { info.GetReturnValue().SetNull(); return; } ByteString bsToEncode = ValueToUTF8String(info.GetIsolate(), argOne); ByteString bsIdentify = ValueToUTF8String(info.GetIsolate(), argTwo); WideString encoded; if (bsIdentify.EqualNoCase("html")) encoded = EncodeHTML(bsToEncode); else if (bsIdentify.EqualNoCase("xml")) encoded = EncodeXML(bsToEncode); else encoded = EncodeURL(bsToEncode); auto result = FX_UTF8Encode(encoded.AsStringView()); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); } // static void CFXJSE_FormCalcContext::Format( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() < 2) { pContext->ThrowParamCountMismatchException("Format"); return; } v8::Local argOne = GetSimpleValue(info, 0); ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne); v8::Local argTwo = GetSimpleValue(info, 1); ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo); CXFA_Document* pDoc = pContext->GetDocument(); CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject()); GCedLocaleIface* pLocale = pThisNode->GetLocale(); WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView()); WideString wsValue = WideString::FromUTF8(bsValue.AsStringView()); auto [bPatternIsString, dwPatternType] = PatternStringType(bsPattern.AsStringView()); if (!bPatternIsString) { switch (dwPatternType) { case CXFA_LocaleValue::ValueType::kDateTime: { auto iTChar = wsPattern.Find(L'T'); if (!iTChar.has_value()) { info.GetReturnValue().SetEmptyString(); return; } auto wsDatePattern = WideString::FromASCII("date{"); wsDatePattern += wsPattern.First(iTChar.value()) + L"} "; auto wsTimePattern = WideString::FromASCII("time{"); wsTimePattern += wsPattern.Last(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}"; wsPattern = wsDatePattern + wsTimePattern; } break; case CXFA_LocaleValue::ValueType::kDate: { wsPattern = L"date{" + wsPattern + L"}"; } break; case CXFA_LocaleValue::ValueType::kTime: { wsPattern = L"time{" + wsPattern + L"}"; } break; case CXFA_LocaleValue::ValueType::kText: { wsPattern = L"text{" + wsPattern + L"}"; } break; case CXFA_LocaleValue::ValueType::kFloat: { wsPattern = L"num{" + wsPattern + L"}"; } break; default: { WideString wsTestPattern = L"num{" + wsPattern + L"}"; CXFA_LocaleValue tempLocaleValue(CXFA_LocaleValue::ValueType::kFloat, wsValue, wsTestPattern, pLocale, pMgr); if (tempLocaleValue.IsValid()) { wsPattern = std::move(wsTestPattern); dwPatternType = CXFA_LocaleValue::ValueType::kFloat; } else { wsPattern = L"text{" + wsPattern + L"}"; dwPatternType = CXFA_LocaleValue::ValueType::kText; } } break; } } CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, pMgr); WideString wsRet; if (!localeValue.FormatPatterns(wsRet, wsPattern, pLocale, XFA_ValuePicture::kDisplay)) { info.GetReturnValue().SetEmptyString(); return; } info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), wsRet.ToUTF8().AsStringView())); } // static void CFXJSE_FormCalcContext::Left( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Left"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); if ((ValueIsNull(info.GetIsolate(), argOne)) || (ValueIsNull(info.GetIsolate(), argTwo))) { info.GetReturnValue().SetNull(); return; } ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo)); info.GetReturnValue().Set(fxv8::NewStringHelper( info.GetIsolate(), bsSource.First(count).AsStringView())); } // static void CFXJSE_FormCalcContext::Len( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Len"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argOne)) { info.GetReturnValue().SetNull(); return; } ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); info.GetReturnValue().Set(static_cast(bsSource.GetLength())); } // static void CFXJSE_FormCalcContext::Lower( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Lower"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argOne)) { info.GetReturnValue().SetNull(); return; } WideTextBuffer szLowBuf; ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); WideString wsArg = WideString::FromUTF8(bsArg.AsStringView()); for (wchar_t ch : wsArg) { if ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0xC0 && ch <= 0xDE)) ch += 32; else if (ch == 0x100 || ch == 0x102 || ch == 0x104) ch += 1; szLowBuf.AppendChar(ch); } auto result = FX_UTF8Encode(szLowBuf.AsStringView()); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); } // static void CFXJSE_FormCalcContext::Ltrim( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Ltrim"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argOne)) { info.GetReturnValue().SetNull(); return; } ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); bsSource.TrimWhitespaceFront(); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView())); } // static void CFXJSE_FormCalcContext::Parse( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 2) { pContext->ThrowParamCountMismatchException("Parse"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); if (ValueIsNull(info.GetIsolate(), argTwo)) { info.GetReturnValue().SetNull(); return; } ByteString bsPattern = ValueToUTF8String(info.GetIsolate(), argOne); ByteString bsValue = ValueToUTF8String(info.GetIsolate(), argTwo); CXFA_Document* pDoc = pContext->GetDocument(); CXFA_LocaleMgr* pMgr = pDoc->GetLocaleMgr(); CXFA_Node* pThisNode = ToNode(pDoc->GetScriptContext()->GetThisObject()); GCedLocaleIface* pLocale = pThisNode->GetLocale(); WideString wsPattern = WideString::FromUTF8(bsPattern.AsStringView()); WideString wsValue = WideString::FromUTF8(bsValue.AsStringView()); auto [bPatternIsString, dwPatternType] = PatternStringType(bsPattern.AsStringView()); if (bPatternIsString) { CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { info.GetReturnValue().SetEmptyString(); return; } auto result = localeValue.GetValue().ToUTF8(); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); return; } switch (dwPatternType) { case CXFA_LocaleValue::ValueType::kDateTime: { auto iTChar = wsPattern.Find(L'T'); if (!iTChar.has_value()) { info.GetReturnValue().SetEmptyString(); return; } WideString wsDatePattern(L"date{" + wsPattern.First(iTChar.value()) + L"} "); WideString wsTimePattern( L"time{" + wsPattern.Last(wsPattern.GetLength() - (iTChar.value() + 1)) + L"}"); wsPattern = wsDatePattern + wsTimePattern; CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { info.GetReturnValue().SetEmptyString(); return; } auto result = localeValue.GetValue().ToUTF8(); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); return; } case CXFA_LocaleValue::ValueType::kDate: { wsPattern = L"date{" + wsPattern + L"}"; CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { info.GetReturnValue().SetEmptyString(); return; } auto result = localeValue.GetValue().ToUTF8(); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); return; } case CXFA_LocaleValue::ValueType::kTime: { wsPattern = L"time{" + wsPattern + L"}"; CXFA_LocaleValue localeValue(dwPatternType, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { info.GetReturnValue().SetEmptyString(); return; } auto result = localeValue.GetValue().ToUTF8(); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); return; } case CXFA_LocaleValue::ValueType::kText: { wsPattern = L"text{" + wsPattern + L"}"; CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { info.GetReturnValue().SetEmptyString(); return; } auto result = localeValue.GetValue().ToUTF8(); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); return; } case CXFA_LocaleValue::ValueType::kFloat: { wsPattern = L"num{" + wsPattern + L"}"; CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat, wsValue, wsPattern, pLocale, pMgr); if (!localeValue.IsValid()) { info.GetReturnValue().SetEmptyString(); return; } info.GetReturnValue().Set(localeValue.GetDoubleNum()); return; } default: { { WideString wsTestPattern = L"num{" + wsPattern + L"}"; CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kFloat, wsValue, wsTestPattern, pLocale, pMgr); if (localeValue.IsValid()) { info.GetReturnValue().Set(localeValue.GetDoubleNum()); return; } } { WideString wsTestPattern = L"text{" + wsPattern + L"}"; CXFA_LocaleValue localeValue(CXFA_LocaleValue::ValueType::kText, wsValue, wsTestPattern, pLocale, pMgr); if (localeValue.IsValid()) { auto result = localeValue.GetValue().ToUTF8(); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), result.AsStringView())); return; } } info.GetReturnValue().SetEmptyString(); return; } } } // static void CFXJSE_FormCalcContext::Replace( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 2 || argc > 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Replace"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); ByteString bsOne; ByteString bsTwo; if (!ValueIsNull(info.GetIsolate(), argOne) && !ValueIsNull(info.GetIsolate(), argTwo)) { bsOne = ValueToUTF8String(info.GetIsolate(), argOne); bsTwo = ValueToUTF8String(info.GetIsolate(), argTwo); } ByteString bsThree; if (argc > 2) { v8::Local argThree = GetSimpleValue(info, 2); bsThree = ValueToUTF8String(info.GetIsolate(), argThree); } bsOne.Replace(bsTwo.AsStringView(), bsThree.AsStringView()); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsOne.AsStringView())); } // static void CFXJSE_FormCalcContext::Right( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Right"); return; } v8::Local argOne = GetSimpleValue(info, 0); v8::Local argTwo = GetSimpleValue(info, 1); if ((ValueIsNull(info.GetIsolate(), argOne)) || (ValueIsNull(info.GetIsolate(), argTwo))) { info.GetReturnValue().SetNull(); return; } ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); int32_t count = std::max(0, ValueToInteger(info.GetIsolate(), argTwo)); info.GetReturnValue().Set(fxv8::NewStringHelper( info.GetIsolate(), bsSource.Last(count).AsStringView())); } // static void CFXJSE_FormCalcContext::Rtrim( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Rtrim"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argOne)) { info.GetReturnValue().SetNull(); return; } ByteString bsSource = ValueToUTF8String(info.GetIsolate(), argOne); bsSource.TrimWhitespaceBack(); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsSource.AsStringView())); } // static void CFXJSE_FormCalcContext::Space( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Space"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (fxv8::IsNull(argOne)) { info.GetReturnValue().SetNull(); return; } int count = std::max(0, ValueToInteger(info.GetIsolate(), argOne)); if (count > kMaxCharCount) { ToFormCalcContext(pThis)->ThrowException("String too long."); return; } DataVector space_string(count, ' '); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(space_string))); } // static void CFXJSE_FormCalcContext::Str( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Str"); return; } v8::Local numberValue = GetSimpleValue(info, 0); if (fxv8::IsNull(numberValue)) { info.GetReturnValue().SetNull(); return; } float fNumber = ValueToFloat(info.GetIsolate(), numberValue); constexpr int32_t kDefaultWidth = 10; int32_t iWidth = kDefaultWidth; if (argc > 1) { v8::Local widthValue = GetSimpleValue(info, 1); iWidth = static_cast(ValueToFloat(info.GetIsolate(), widthValue)); if (iWidth > kMaxCharCount) { ToFormCalcContext(pThis)->ThrowException("String too long."); return; } } constexpr int32_t kDefaultPrecision = 0; int32_t iPrecision = kDefaultPrecision; if (argc > 2) { constexpr int32_t kMaxPrecision = 15; v8::Local precision_value = GetSimpleValue(info, 2); iPrecision = std::max(0, static_cast(ValueToFloat( info.GetIsolate(), precision_value))); iPrecision = std::min(iPrecision, kMaxPrecision); } ByteString bsFormat = "%"; if (iPrecision) { bsFormat += "."; bsFormat += ByteString::FormatInteger(iPrecision); } bsFormat += "f"; ByteString bsNumber = ByteString::Format(bsFormat.c_str(), fNumber); const char* pData = bsNumber.c_str(); int32_t iLength = bsNumber.GetLength(); int32_t u = 0; UNSAFE_TODO({ while (u < iLength) { if (pData[u] == '.') { break; } ++u; } if (u > iWidth || (iPrecision + u) >= iWidth) { DataVector stars(std::max(iWidth, 0), '*'); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(stars))); return; } ByteString resultBuf; if (u == iLength) { if (iLength > iWidth) { int32_t i = 0; while (i < iWidth) { resultBuf += '*'; ++i; } } else { int32_t i = 0; while (i < iWidth - iLength) { resultBuf += ' '; ++i; } resultBuf += pData; } info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView())); return; } int32_t iLeavingSpace = iWidth - u - iPrecision; if (iPrecision != 0) { iLeavingSpace--; } int32_t i = 0; while (i < iLeavingSpace) { resultBuf += ' '; ++i; } i = 0; while (i < u) { resultBuf += pData[i]; ++i; } if (iPrecision != 0) { resultBuf += '.'; } u++; i = 0; while (u < iLength) { if (i >= iPrecision) { break; } resultBuf += pData[u]; ++i; ++u; } while (i < iPrecision) { resultBuf += '0'; ++i; } info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), resultBuf.AsStringView())); }); } // static void CFXJSE_FormCalcContext::Stuff( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 3 || argc > 4) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Stuff"); return; } v8::Local sourceValue = GetSimpleValue(info, 0); v8::Local startValue = GetSimpleValue(info, 1); v8::Local deleteValue = GetSimpleValue(info, 2); if (fxv8::IsNull(sourceValue) || fxv8::IsNull(startValue) || fxv8::IsNull(deleteValue)) { info.GetReturnValue().SetNull(); return; } int32_t iStart = 1; // one-based character indexing. int32_t iDelete = 0; ByteString bsSource = ValueToUTF8String(info.GetIsolate(), sourceValue); int32_t iLength = pdfium::checked_cast(bsSource.GetLength()); if (iLength) { iStart = std::clamp( static_cast(ValueToFloat(info.GetIsolate(), startValue)), 1, iLength); iDelete = std::clamp( static_cast(ValueToFloat(info.GetIsolate(), deleteValue)), 0, iLength - iStart + 1); } ByteString bsInsert; if (argc > 3) { v8::Local insertValue = GetSimpleValue(info, 3); bsInsert = ValueToUTF8String(info.GetIsolate(), insertValue); } --iStart; // now zero-based. ByteString bsResult = {bsSource.AsStringView().First(iStart), bsInsert.AsStringView(), bsSource.AsStringView().Substr(iStart + iDelete)}; info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsResult.AsStringView())); } // static void CFXJSE_FormCalcContext::Substr( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Substr"); return; } v8::Local string_value = GetSimpleValue(info, 0); v8::Local start_value = GetSimpleValue(info, 1); v8::Local end_value = GetSimpleValue(info, 2); if (ValueIsNull(info.GetIsolate(), string_value) || ValueIsNull(info.GetIsolate(), start_value) || ValueIsNull(info.GetIsolate(), end_value)) { info.GetReturnValue().SetNull(); return; } ByteString bsSource = ValueToUTF8String(info.GetIsolate(), string_value); size_t iLength = bsSource.GetLength(); if (iLength == 0) { info.GetReturnValue().SetEmptyString(); return; } // |start_value| is 1-based. Assume first character if |start_value| is less // than 1, per spec. Subtract 1 since |iStart| is 0-based. size_t iStart = std::max(ValueToInteger(info.GetIsolate(), start_value), 1) - 1; if (iStart >= iLength) { info.GetReturnValue().SetEmptyString(); return; } // Negative values are treated as 0. Can't clamp() due to sign mismatches. size_t iCount = std::max(ValueToInteger(info.GetIsolate(), end_value), 0); iCount = std::min(iCount, iLength - iStart); info.GetReturnValue().Set(fxv8::NewStringHelper( info.GetIsolate(), bsSource.Substr(iStart, iCount).AsStringView())); } // static void CFXJSE_FormCalcContext::Uuid( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 0 || argc > 1) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Uuid"); return; } int32_t iNum = 0; if (argc > 0) { v8::Local argOne = GetSimpleValue(info, 0); iNum = static_cast(ValueToFloat(info.GetIsolate(), argOne)); } info.GetReturnValue().Set(fxv8::NewStringHelper( info.GetIsolate(), GUIDString(!!iNum).AsStringView())); } // static void CFXJSE_FormCalcContext::Upper( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 2) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("Upper"); return; } v8::Local argOne = GetSimpleValue(info, 0); if (ValueIsNull(info.GetIsolate(), argOne)) { info.GetReturnValue().SetNull(); return; } ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); WideString wsArg = WideString::FromUTF8(bsArg.AsStringView()); WideString upperStringBuf; upperStringBuf.Reserve(wsArg.GetLength()); for (wchar_t ch : wsArg) { if ((ch >= 0x61 && ch <= 0x7A) || (ch >= 0xE0 && ch <= 0xFE)) ch -= 32; else if (ch == 0x101 || ch == 0x103 || ch == 0x105) ch -= 1; upperStringBuf += ch; } info.GetReturnValue().Set(fxv8::NewStringHelper( info.GetIsolate(), FX_UTF8Encode(upperStringBuf.AsStringView()).AsStringView())); } // static void CFXJSE_FormCalcContext::WordNum( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { int32_t argc = info.Length(); if (argc < 1 || argc > 3) { ToFormCalcContext(pThis)->ThrowParamCountMismatchException("WordNum"); return; } v8::Local numberValue = GetSimpleValue(info, 0); if (fxv8::IsNull(numberValue)) { info.GetReturnValue().SetNull(); return; } float fNumber = ValueToFloat(info.GetIsolate(), numberValue); int32_t iIdentifier = 0; if (argc > 1) { v8::Local identifierValue = GetSimpleValue(info, 1); if (fxv8::IsNull(identifierValue)) { info.GetReturnValue().SetNull(); return; } iIdentifier = static_cast(ValueToFloat(info.GetIsolate(), identifierValue)); } ByteString bsLocale; if (argc > 2) { v8::Local localeValue = GetSimpleValue(info, 2); if (fxv8::IsNull(localeValue)) { info.GetReturnValue().SetNull(); return; } bsLocale = ValueToUTF8String(info.GetIsolate(), localeValue); } if (isnan(fNumber) || fNumber < 0.0f || fNumber > 922337203685477550.0f) { info.GetReturnValue().Set(fxv8::NewStringHelper(info.GetIsolate(), "*")); return; } ByteString bsFormatted = ByteString::Format("%.2f", fNumber); ByteString bsWorded = WordUS(bsFormatted.AsStringView(), iIdentifier); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), bsWorded.AsStringView())); } // static void CFXJSE_FormCalcContext::Get( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 1) { pContext->ThrowParamCountMismatchException("Get"); return; } CXFA_Document* pDoc = pContext->GetDocument(); if (!pDoc) return; CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider(); if (!pAppProvider) return; v8::Local argOne = GetSimpleValue(info, 0); ByteString bsUrl = ValueToUTF8String(info.GetIsolate(), argOne); RetainPtr pFile = pAppProvider->DownloadURL(WideString::FromUTF8(bsUrl.AsStringView())); if (!pFile) return; FX_FILESIZE size = pFile->GetSize(); DataVector data_buf(size); // TODO(tsepez): check return value? (void)pFile->ReadBlockAtOffset(data_buf, 0); info.GetReturnValue().Set( fxv8::NewStringHelper(info.GetIsolate(), ByteStringView(data_buf))); } // static void CFXJSE_FormCalcContext::Post( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); int32_t argc = info.Length(); if (argc < 2 || argc > 5) { pContext->ThrowParamCountMismatchException("Post"); return; } CXFA_Document* pDoc = pContext->GetDocument(); if (!pDoc) return; CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider(); if (!pAppProvider) return; v8::Local argOne = GetSimpleValue(info, 0); ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne); v8::Local argTwo = GetSimpleValue(info, 1); ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo); ByteString bsContentType; if (argc > 2) { v8::Local argThree = GetSimpleValue(info, 2); bsContentType = ValueToUTF8String(info.GetIsolate(), argThree); } ByteString bsEncode; if (argc > 3) { v8::Local argFour = GetSimpleValue(info, 3); bsEncode = ValueToUTF8String(info.GetIsolate(), argFour); } ByteString bsHeader; if (argc > 4) { v8::Local argFive = GetSimpleValue(info, 4); bsHeader = ValueToUTF8String(info.GetIsolate(), argFive); } WideString decodedResponse; if (!pAppProvider->PostRequestURL( WideString::FromUTF8(bsURL.AsStringView()), WideString::FromUTF8(bsData.AsStringView()), WideString::FromUTF8(bsContentType.AsStringView()), WideString::FromUTF8(bsEncode.AsStringView()), WideString::FromUTF8(bsHeader.AsStringView()), decodedResponse)) { pContext->ThrowServerDeniedException(); return; } info.GetReturnValue().Set(fxv8::NewStringHelper( info.GetIsolate(), decodedResponse.ToUTF8().AsStringView())); } // static void CFXJSE_FormCalcContext::Put( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); int32_t argc = info.Length(); if (argc < 2 || argc > 3) { pContext->ThrowParamCountMismatchException("Put"); return; } CXFA_Document* pDoc = pContext->GetDocument(); if (!pDoc) return; CXFA_FFApp::CallbackIface* pAppProvider = pDoc->GetNotify()->GetAppProvider(); if (!pAppProvider) return; v8::Local argOne = GetSimpleValue(info, 0); ByteString bsURL = ValueToUTF8String(info.GetIsolate(), argOne); v8::Local argTwo = GetSimpleValue(info, 1); ByteString bsData = ValueToUTF8String(info.GetIsolate(), argTwo); ByteString bsEncode; if (argc > 2) { v8::Local argThree = GetSimpleValue(info, 2); bsEncode = ValueToUTF8String(info.GetIsolate(), argThree); } if (!pAppProvider->PutRequestURL( WideString::FromUTF8(bsURL.AsStringView()), WideString::FromUTF8(bsData.AsStringView()), WideString::FromUTF8(bsEncode.AsStringView()))) { pContext->ThrowServerDeniedException(); return; } info.GetReturnValue().SetEmptyString(); } // static void CFXJSE_FormCalcContext::assign_value_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { v8::Isolate* pIsolate = info.GetIsolate(); CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 2) { pContext->ThrowCompilerErrorException(); return; } ByteStringView bsFuncName("asgn_val_op"); v8::Local lValue = info[0]; v8::Local rValue = GetSimpleValue(info, 1); if (fxv8::IsArray(lValue)) { v8::Local arr = lValue.As(); uint32_t iLeftLength = fxv8::GetArrayLengthHelper(arr); v8::Local propertyValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1); for (uint32_t i = 2; i < iLeftLength; i++) { v8::Local jsValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, i); if (!fxv8::IsObject(jsValue)) { pContext->ThrowNoDefaultPropertyException(bsFuncName); return; } v8::Local jsObjectValue = jsValue.As(); if (fxv8::IsNull(propertyValue)) { if (!SetObjectDefaultValue(pIsolate, jsObjectValue, rValue)) { pContext->ThrowNoDefaultPropertyException(bsFuncName); return; } } else { fxv8::ReentrantPutObjectPropertyHelper( pIsolate, jsObjectValue, fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue) .AsStringView(), rValue); } } } else if (fxv8::IsObject(lValue)) { if (!SetObjectDefaultValue(pIsolate, lValue.As(), rValue)) { pContext->ThrowNoDefaultPropertyException(bsFuncName); return; } } info.GetReturnValue().Set(rValue); } // static void CFXJSE_FormCalcContext::logical_or_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { info.GetReturnValue().SetNull(); return; } float first = ValueToFloat(info.GetIsolate(), argFirst); float second = ValueToFloat(info.GetIsolate(), argSecond); info.GetReturnValue().Set(static_cast(first || second)); } // static void CFXJSE_FormCalcContext::logical_and_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { info.GetReturnValue().SetNull(); return; } float first = ValueToFloat(info.GetIsolate(), argFirst); float second = ValueToFloat(info.GetIsolate(), argSecond); info.GetReturnValue().Set(static_cast(first && second)); } // static void CFXJSE_FormCalcContext::equality_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } if (fm_ref_equal(pThis, info)) { info.GetReturnValue().Set(1); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { info.GetReturnValue().Set( static_cast(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond))); return; } if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { info.GetReturnValue().Set(static_cast( fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) == fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond))); return; } double first = ValueToDouble(info.GetIsolate(), argFirst); double second = ValueToDouble(info.GetIsolate(), argSecond); info.GetReturnValue().Set(static_cast(first == second)); } // static void CFXJSE_FormCalcContext::notequality_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } if (fm_ref_equal(pThis, info)) { info.GetReturnValue().Set(0); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { info.GetReturnValue().Set( static_cast(!fxv8::IsNull(argFirst) || !fxv8::IsNull(argSecond))); return; } if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { info.GetReturnValue().Set(static_cast( fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst) != fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond))); return; } double first = ValueToDouble(info.GetIsolate(), argFirst); double second = ValueToDouble(info.GetIsolate(), argSecond); info.GetReturnValue().Set(static_cast(first != second)); } // static bool CFXJSE_FormCalcContext::fm_ref_equal( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { v8::Local argFirst = info[0]; v8::Local argSecond = info[1]; if (!fxv8::IsArray(argFirst) || !fxv8::IsArray(argSecond)) return false; v8::Local firstArr = argFirst.As(); v8::Local secondArr = argSecond.As(); v8::Local firstFlag = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 0); v8::Local secondFlag = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 0); if (fxv8::ReentrantToInt32Helper(info.GetIsolate(), firstFlag) != 3 || fxv8::ReentrantToInt32Helper(info.GetIsolate(), secondFlag) != 3) { return false; } v8::Local firstValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), firstArr, 2); v8::Local secondValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), secondArr, 2); if (fxv8::IsNull(firstValue) || fxv8::IsNull(secondValue)) return false; return FXJSE_RetrieveObjectBinding(firstValue) == FXJSE_RetrieveObjectBinding(secondValue); } // static void CFXJSE_FormCalcContext::less_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { info.GetReturnValue().Set(0); return; } if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { ByteString bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst); ByteString bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond); info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) < 0); return; } double first = ValueToDouble(info.GetIsolate(), argFirst); double second = ValueToDouble(info.GetIsolate(), argSecond); info.GetReturnValue().Set(static_cast(first < second)); } // static void CFXJSE_FormCalcContext::lessequal_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { info.GetReturnValue().Set( static_cast(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond))); return; } if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst); auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond); info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) <= 0); return; } double first = ValueToDouble(info.GetIsolate(), argFirst); double second = ValueToDouble(info.GetIsolate(), argSecond); info.GetReturnValue().Set(static_cast(first <= second)); } // static void CFXJSE_FormCalcContext::greater_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { info.GetReturnValue().Set(0); return; } if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst); auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond); info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) > 0); return; } double first = ValueToDouble(info.GetIsolate(), argFirst); double second = ValueToDouble(info.GetIsolate(), argSecond); info.GetReturnValue().Set(static_cast(first > second)); } // static void CFXJSE_FormCalcContext::greaterequal_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) || fxv8::IsNull(argSecond)) { info.GetReturnValue().Set( static_cast(fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond))); return; } if (fxv8::IsString(argFirst) && fxv8::IsString(argSecond)) { auto bs1 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argFirst); auto bs2 = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), argSecond); info.GetReturnValue().Set(bs1.Compare(bs2.AsStringView()) >= 0); return; } double first = ValueToDouble(info.GetIsolate(), argFirst); double second = ValueToDouble(info.GetIsolate(), argSecond); info.GetReturnValue().Set(static_cast(first >= second)); } // static void CFXJSE_FormCalcContext::plus_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } if (ValueIsNull(info.GetIsolate(), info[0]) && ValueIsNull(info.GetIsolate(), info[1])) { info.GetReturnValue().SetNull(); return; } const double first = ValueToDouble(info.GetIsolate(), info[0]); const double second = ValueToDouble(info.GetIsolate(), info[1]); info.GetReturnValue().Set(first + second); } // static void CFXJSE_FormCalcContext::minus_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { info.GetReturnValue().SetNull(); return; } double first = ValueToDouble(info.GetIsolate(), argFirst); double second = ValueToDouble(info.GetIsolate(), argSecond); info.GetReturnValue().Set(first - second); } // static void CFXJSE_FormCalcContext::multiple_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { info.GetReturnValue().SetNull(); return; } double first = ValueToDouble(info.GetIsolate(), argFirst); double second = ValueToDouble(info.GetIsolate(), argSecond); info.GetReturnValue().Set(first * second); } // static void CFXJSE_FormCalcContext::divide_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 2) { pContext->ThrowCompilerErrorException(); return; } v8::Local argFirst = GetSimpleValue(info, 0); v8::Local argSecond = GetSimpleValue(info, 1); if (fxv8::IsNull(argFirst) && fxv8::IsNull(argSecond)) { info.GetReturnValue().SetNull(); return; } double second = ValueToDouble(info.GetIsolate(), argSecond); if (second == 0.0) { pContext->ThrowDivideByZeroException(); return; } double first = ValueToDouble(info.GetIsolate(), argFirst); info.GetReturnValue().Set(first / second); } // static void CFXJSE_FormCalcContext::positive_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argOne = GetSimpleValue(info, 0); if (fxv8::IsNull(argOne)) { info.GetReturnValue().SetNull(); return; } info.GetReturnValue().Set(0.0 + ValueToDouble(info.GetIsolate(), argOne)); } // static void CFXJSE_FormCalcContext::negative_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argOne = GetSimpleValue(info, 0); if (fxv8::IsNull(argOne)) { info.GetReturnValue().SetNull(); return; } info.GetReturnValue().Set(0.0 - ValueToDouble(info.GetIsolate(), argOne)); } // static void CFXJSE_FormCalcContext::logical_not_operator( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argOne = GetSimpleValue(info, 0); if (fxv8::IsNull(argOne)) { info.GetReturnValue().SetNull(); return; } double first = ValueToDouble(info.GetIsolate(), argOne); info.GetReturnValue().Set((first == 0.0) ? 1 : 0); } // static void CFXJSE_FormCalcContext::dot_accessor( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { DotAccessorCommon(pThis, info, /*bDotAccessor=*/true); } // static void CFXJSE_FormCalcContext::dotdot_accessor( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { DotAccessorCommon(pThis, info, /*bDotAccessor=*/false); } // static void CFXJSE_FormCalcContext::eval_translation( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 1) { pContext->ThrowParamCountMismatchException("Eval"); return; } v8::Local argOne = GetSimpleValue(info, 0); ByteString bsArg = ValueToUTF8String(info.GetIsolate(), argOne); if (bsArg.IsEmpty()) { pContext->ThrowArgumentMismatchException(); return; } WideString wsCalcScript = WideString::FromUTF8(bsArg.AsStringView()); std::optional wsJavaScriptBuf = CFXJSE_FormCalcContext::Translate(pContext->GetDocument()->GetHeap(), wsCalcScript.AsStringView()); if (!wsJavaScriptBuf.has_value()) { pContext->ThrowCompilerErrorException(); return; } info.GetReturnValue().Set(fxv8::NewStringHelper( info.GetIsolate(), FX_UTF8Encode(wsJavaScriptBuf.value().AsStringView()).AsStringView())); } // static void CFXJSE_FormCalcContext::is_fm_object( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { const bool result = info.Length() == 1 && fxv8::IsObject(info[0]); info.GetReturnValue().Set(result); } // static void CFXJSE_FormCalcContext::is_fm_array( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { const bool result = info.Length() == 1 && fxv8::IsArray(info[0]); info.GetReturnValue().Set(result); } // static void CFXJSE_FormCalcContext::get_fm_value( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 1) { pContext->ThrowCompilerErrorException(); return; } v8::Local argOne = info[0]; if (fxv8::IsArray(argOne)) { v8::Local arr = argOne.As(); v8::Local propertyValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 1); v8::Local jsValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2); if (!fxv8::IsObject(jsValue)) { info.GetReturnValue().Set(fxv8::NewUndefinedHelper(info.GetIsolate())); return; } v8::Local jsObjectValue = jsValue.As(); if (fxv8::IsNull(propertyValue)) { info.GetReturnValue().Set( GetObjectDefaultValue(info.GetIsolate(), jsObjectValue)); return; } ByteString bsName = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), propertyValue); info.GetReturnValue().Set(fxv8::ReentrantGetObjectPropertyHelper( info.GetIsolate(), jsObjectValue, bsName.AsStringView())); return; } if (fxv8::IsObject(argOne)) { v8::Local obj = argOne.As(); info.GetReturnValue().Set(GetObjectDefaultValue(info.GetIsolate(), obj)); return; } info.GetReturnValue().Set(argOne); } // static void CFXJSE_FormCalcContext::get_fm_jsobj( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { ToFormCalcContext(pThis)->ThrowCompilerErrorException(); return; } v8::Local argOne = info[0]; if (!fxv8::IsArray(argOne)) { info.GetReturnValue().Set(argOne); return; } v8::Local arr = argOne.As(); info.GetReturnValue().Set( fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2)); } // static void CFXJSE_FormCalcContext::fm_var_filter( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); if (info.Length() != 1) { pContext->ThrowCompilerErrorException(); return; } v8::Local argOne = info[0]; if (!fxv8::IsArray(argOne)) { info.GetReturnValue().Set(GetSimpleValue(info, 0)); return; } v8::Local arr = argOne.As(); v8::Local flagsValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 0); int32_t iFlags = fxv8::ReentrantToInt32Helper(info.GetIsolate(), flagsValue); if (iFlags != 3 && iFlags != 4) { info.GetReturnValue().Set(GetSimpleValue(info, 0)); return; } if (iFlags == 4) { v8::LocalVector values(info.GetIsolate(), 3); values[0] = fxv8::NewNumberHelper(info.GetIsolate(), 3); values[1] = fxv8::NewNullHelper(info.GetIsolate()); values[2] = fxv8::NewNullHelper(info.GetIsolate()); info.GetReturnValue().Set(fxv8::NewArrayHelper(info.GetIsolate(), values)); return; } v8::Local objectValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, 2); if (fxv8::IsNull(objectValue)) { pContext->ThrowCompilerErrorException(); return; } info.GetReturnValue().Set(argOne); } // static void CFXJSE_FormCalcContext::concat_fm_object( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info) { v8::Isolate* pIsolate = ToFormCalcContext(pThis)->GetIsolate(); v8::LocalVector returnValues(pIsolate); for (int i = 0; i < info.Length(); ++i) { if (fxv8::IsArray(info[i])) { v8::Local arr = info[i].As(); uint32_t length = fxv8::GetArrayLengthHelper(arr); for (uint32_t j = 2; j < length; j++) { returnValues.push_back( fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, j)); } } returnValues.push_back(info[i]); } info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, returnValues)); } // static ByteString CFXJSE_FormCalcContext::GenerateSomExpression(ByteStringView bsName, int32_t iIndexFlags, int32_t iIndexValue, bool bIsStar) { if (bIsStar) return ByteString(bsName, "[*]"); // `iIndexFlags` values are the same as enum class // `CXFA_FMIndexExpression::AccessorIndex` values. if (iIndexFlags == 0) return ByteString(bsName); if (iIndexFlags == 1 || iIndexValue == 0) { return ByteString(bsName, "[") + ByteString::FormatInteger(iIndexValue) + "]"; } const bool bNegative = iIndexValue < 0; ByteString bsSomExp(bsName); if (iIndexFlags == 2) { bsSomExp += bNegative ? "[-" : "[+"; } else { DCHECK_EQ(iIndexFlags, 3); bsSomExp += bNegative ? "[" : "[-"; } FX_SAFE_INT32 safe_index = iIndexValue; if (bNegative) safe_index = -safe_index; bsSomExp += ByteString::FormatInteger(safe_index.ValueOrDefault(0)); bsSomExp += "]"; return bsSomExp; } std::optional CFXJSE_FormCalcContext::Translate( cppgc::Heap* pHeap, WideStringView wsFormcalc) { if (wsFormcalc.IsEmpty()) return WideTextBuffer(); CXFA_FMLexer lexer(wsFormcalc); CXFA_FMParser parser(pHeap, &lexer); CXFA_FMAST* ast = parser.Parse(); if (!ast || parser.HasError()) return std::nullopt; CXFA_FMToJavaScriptDepth::Reset(); std::optional wsJavaScript = ast->ToJavaScript(); if (!wsJavaScript.has_value()) return std::nullopt; if (CXFA_IsTooBig(wsJavaScript.value())) return std::nullopt; return wsJavaScript; } CFXJSE_FormCalcContext::CFXJSE_FormCalcContext(v8::Isolate* pIsolate, CFXJSE_Context* pScriptContext, CXFA_Document* pDoc) : m_pIsolate(pIsolate), m_pDocument(pDoc) { m_Value.Reset(m_pIsolate, NewBoundV8Object( m_pIsolate, CFXJSE_Class::Create( pScriptContext, &kFormCalcDescriptor, false) ->GetTemplate(m_pIsolate))); } CFXJSE_FormCalcContext::~CFXJSE_FormCalcContext() = default; CFXJSE_FormCalcContext* CFXJSE_FormCalcContext::AsFormCalcContext() { return this; } v8::Local CFXJSE_FormCalcContext::GlobalPropertyGetter() { return v8::Local::New(m_pIsolate, m_Value); } // static void CFXJSE_FormCalcContext::DotAccessorCommon( CFXJSE_HostObject* pThis, const v8::FunctionCallbackInfo& info, bool bDotAccessor) { CFXJSE_FormCalcContext* pContext = ToFormCalcContext(pThis); v8::Isolate* pIsolate = pContext->GetIsolate(); int32_t argc = info.Length(); if (argc < 4 || argc > 5) { pContext->ThrowCompilerErrorException(); return; } bool bIsStar = true; int32_t iIndexValue = 0; if (argc > 4) { bIsStar = false; iIndexValue = ValueToInteger(info.GetIsolate(), info[4]); } const ByteString bsName = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[2]); const bool bHasNoResolveName = bDotAccessor && bsName.IsEmpty(); ByteString bsSomExp = GenerateSomExpression( bsName.AsStringView(), fxv8::ReentrantToInt32Helper(info.GetIsolate(), info[3]), iIndexValue, bIsStar); v8::Local argAccessor = info[0]; if (fxv8::IsArray(argAccessor)) { v8::Local arr = argAccessor.As(); uint32_t iLength = fxv8::GetArrayLengthHelper(arr); if (iLength < 3) { pContext->ThrowArgumentMismatchException(); return; } // TODO(crbug.com/pdfium/2090) - doublecheck use of std::vector std::vector> resolveValues( iLength - 2, v8::LocalVector(info.GetIsolate())); bool bAttribute = false; bool bAllEmpty = true; for (uint32_t i = 2; i < iLength; i++) { v8::Local hJSObjValue = fxv8::ReentrantGetArrayElementHelper(info.GetIsolate(), arr, i); std::optional maybeResult = ResolveObjects(pThis, hJSObjValue, bsSomExp.AsStringView(), bDotAccessor, bHasNoResolveName); if (maybeResult.has_value()) { resolveValues[i - 2] = ParseResolveResult(pThis, maybeResult.value(), hJSObjValue, &bAttribute); bAllEmpty = bAllEmpty && resolveValues[i - 2].empty(); } } if (bAllEmpty) { pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(), bsSomExp.AsStringView()); return; } v8::LocalVector values(pIsolate); values.push_back(fxv8::NewNumberHelper(pIsolate, 1)); values.push_back( bAttribute ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView()) .As() : fxv8::NewNullHelper(pIsolate).As()); for (uint32_t i = 0; i < iLength - 2; i++) { for (size_t j = 0; j < resolveValues[i].size(); j++) values.push_back(resolveValues[i][j]); } info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values)); return; } std::optional maybeResult; ByteString bsAccessorName = fxv8::ReentrantToByteStringHelper(info.GetIsolate(), info[1]); if (fxv8::IsObject(argAccessor) || (fxv8::IsNull(argAccessor) && bsAccessorName.IsEmpty())) { maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(), bDotAccessor, bHasNoResolveName); } else if (!fxv8::IsObject(argAccessor) && !bsAccessorName.IsEmpty()) { v8::Local obj = GetObjectForName(pThis, bsAccessorName.AsStringView()); if (!obj.IsEmpty()) { argAccessor = obj; maybeResult = ResolveObjects(pThis, argAccessor, bsSomExp.AsStringView(), bDotAccessor, bHasNoResolveName); } } if (!maybeResult.has_value()) { pContext->ThrowPropertyNotInObjectException(bsName.AsStringView(), bsSomExp.AsStringView()); return; } bool bAttribute = false; v8::LocalVector resolveValues = ParseResolveResult(pThis, maybeResult.value(), argAccessor, &bAttribute); v8::LocalVector values(pIsolate, resolveValues.size() + 2); values[0] = fxv8::NewNumberHelper(pIsolate, 1); values[1] = bAttribute ? fxv8::NewStringHelper(pIsolate, bsName.AsStringView()) .As() : fxv8::NewNullHelper(pIsolate).As(); for (size_t i = 0; i < resolveValues.size(); i++) values[i + 2] = resolveValues[i]; info.GetReturnValue().Set(fxv8::NewArrayHelper(pIsolate, values)); } // static bool CFXJSE_FormCalcContext::IsIsoDateFormat(ByteStringView bsData, int32_t* pYear, int32_t* pMonth, int32_t* pDay) { pdfium::span pData = bsData.span(); int32_t& iYear = *pYear; int32_t& iMonth = *pMonth; int32_t& iDay = *pDay; iYear = 0; iMonth = 1; iDay = 1; if (pData.size() < 4) { return false; } std::array szYear = {}; for (int32_t i = 0; i < 4; ++i) { if (!isdigit(pData[i])) { return false; } szYear[i] = pData[i]; } iYear = FXSYS_atoi(szYear.data()); if (pData.size() == 4) { return true; } int32_t iStyle = pData[4] == '-' ? 1 : 0; size_t iPosOff = iStyle == 0 ? 4 : 5; if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1])) { return false; } char szBuffer[3] = {}; szBuffer[0] = pData[iPosOff]; szBuffer[1] = pData[iPosOff + 1]; iMonth = FXSYS_atoi(szBuffer); if (iMonth > 12 || iMonth < 1) { return false; } if (iStyle == 0) { iPosOff += 2; if (pData.size() == 6) { return true; } } else { iPosOff += 3; if (pData.size() == 7) { return true; } } if (!isdigit(pData[iPosOff]) || !isdigit(pData[iPosOff + 1])) { return false; } szBuffer[0] = pData[iPosOff]; szBuffer[1] = pData[iPosOff + 1]; iDay = FXSYS_atoi(szBuffer); if (iPosOff + 2 < pData.size()) { return false; } if (iMonth == 2) { bool bIsLeap = (!(iYear % 4) && (iYear % 100)) || !(iYear % 400); return iDay <= (bIsLeap ? 29 : 28); } if (iMonth < 8) { return iDay <= (iMonth % 2 == 0 ? 30 : 31); } return iDay <= (iMonth % 2 == 0 ? 31 : 30); } // static bool CFXJSE_FormCalcContext::IsIsoTimeFormat(ByteStringView bsData) { enum State { kHour, kMinute, kSecond, kZoneHour, kZoneMinute, kFinished }; pdfium::span pData = bsData.span(); if (pData.empty()) { return false; } size_t iZone = 0; size_t i = 0; while (i < pData.size()) { if (!isdigit(pData[i]) && pData[i] != ':') { iZone = i; break; } ++i; } if (i == pData.size()) { iZone = pData.size(); } char szBuffer[3] = {}; // Last char always stays NUL for termination. State state = kHour; size_t iIndex = 0; while (iIndex + 1 < iZone) { szBuffer[0] = pData[iIndex]; szBuffer[1] = pData[iIndex + 1]; if (!isdigit(szBuffer[0]) || !isdigit(szBuffer[1])) { return false; } int32_t value = FXSYS_atoi(szBuffer); if (state == kHour) { if (value >= 24) { return false; } state = kMinute; } else if (state == kMinute) { if (value >= 60) { return false; } state = kSecond; } else if (state == kSecond) { // Allow leap second. if (value > 60) { return false; } state = kFinished; } else { return false; } iIndex += 2; if (iIndex < iZone && pData[iIndex] == ':') { ++iIndex; } } if (iIndex < pData.size() && pData[iIndex] == '.') { constexpr int kSubSecondLength = 3; if (iIndex + kSubSecondLength >= pData.size()) { return false; } ++iIndex; std::array szMilliSeconds = {}; for (int j = 0; j < kSubSecondLength; ++j) { char c = pData[iIndex + j]; if (!isdigit(c)) { return false; } szMilliSeconds[j] = c; } if (FXSYS_atoi(szMilliSeconds.data()) >= 1000) { return false; } iIndex += kSubSecondLength; } if (iIndex < pData.size() && FXSYS_towlower(pData[iIndex]) == 'z') { return true; } if (iIndex < pData.size()) { if (pData[iIndex] == '+') { ++iIndex; } else if (pData[iIndex] == '-') { ++iIndex; } } state = kZoneHour; while (iIndex + 1 < pData.size()) { szBuffer[0] = pData[iIndex]; szBuffer[1] = pData[iIndex + 1]; if (!isdigit(szBuffer[0]) || !isdigit(szBuffer[1])) { return false; } int32_t value = FXSYS_atoi(szBuffer); if (state == kZoneHour) { if (value >= 24) { return false; } state = kZoneMinute; } else if (state == kZoneMinute) { if (value >= 60) { return false; } state = kFinished; } else { return false; } iIndex += 2; if (iIndex < pData.size() && pData[iIndex] == ':') { ++iIndex; } } // Success if all input was processed. return iIndex == pData.size(); } bool CFXJSE_FormCalcContext::IsIsoDateTimeFormat(ByteStringView bsData, int32_t* pYear, int32_t* pMonth, int32_t* pDay) { *pYear = 0; *pMonth = 0; *pDay = 0; size_t iIndex = 0; while (iIndex < bsData.GetLength()) { if (bsData[iIndex] == 'T' || bsData[iIndex] == 't') { break; } ++iIndex; } if (iIndex == bsData.GetLength() || (iIndex != 8 && iIndex != 10)) { return false; } ByteStringView date_part = bsData.First(iIndex); ByteStringView time_part = bsData.Substr(iIndex + 1); return IsIsoDateFormat(date_part, pYear, pMonth, pDay) && IsIsoTimeFormat(time_part); } // static int32_t CFXJSE_FormCalcContext::DateString2Num(ByteStringView bsDate) { int32_t iYear = 0; int32_t iMonth = 0; int32_t iDay = 0; if (bsDate.GetLength() <= 10) { if (!IsIsoDateFormat(bsDate, &iYear, &iMonth, &iDay)) { return 0; } } else { if (!IsIsoDateTimeFormat(bsDate, &iYear, &iMonth, &iDay)) { return 0; } } float dDays = 0; int32_t i = 1; if (iYear < 1900) { return 0; } while (iYear - i >= 1900) { dDays += ((!((iYear - i) % 4) && ((iYear - i) % 100)) || !((iYear - i) % 400)) ? 366 : 365; ++i; } i = 1; while (i < iMonth) { if (i == 2) { dDays += ((!(iYear % 4) && (iYear % 100)) || !(iYear % 400)) ? 29 : 28; } else if (i <= 7) { dDays += (i % 2 == 0) ? 30 : 31; } else { dDays += (i % 2 == 0) ? 31 : 30; } ++i; } i = 0; while (iDay - i > 0) { ++dDays; ++i; } return static_cast(dDays); } bool CFXJSE_FormCalcContext::ApplyToExpansion( std::function)> fn, const v8::FunctionCallbackInfo& info, bool bStrict) { v8::Isolate* pIsolate = info.GetIsolate(); for (int32_t i = 0; i < info.Length(); i++) { v8::Local argValue = info[i]; if (fxv8::IsArray(argValue)) { if (!ApplyToArray(pIsolate, fn, argValue.As()) && bStrict) { ThrowArgumentMismatchException(); return false; } } else if (fxv8::IsObject(argValue)) { ApplyToObject(pIsolate, fn, argValue.As()); } else if (!fxv8::IsNull(argValue)) { fn(pIsolate, argValue); } } return true; } bool CFXJSE_FormCalcContext::ApplyToArray( v8::Isolate* pIsolate, std::function)> fn, v8::Local pArray) { uint32_t iLength = fxv8::GetArrayLengthHelper(pArray); if (iLength < 3) return false; v8::Local propertyValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, 1); ByteString bsName; const bool nullprop = fxv8::IsNull(propertyValue); if (!nullprop) bsName = fxv8::ReentrantToByteStringHelper(pIsolate, propertyValue); for (uint32_t j = 2; j < iLength; j++) { v8::Local jsValue = fxv8::ReentrantGetArrayElementHelper(pIsolate, pArray, j); if (!fxv8::IsObject(jsValue)) continue; v8::Local jsObjectValue = jsValue.As(); v8::Local newPropertyValue = nullprop ? GetObjectDefaultValue(pIsolate, jsObjectValue) : fxv8::ReentrantGetObjectPropertyHelper( pIsolate, jsObjectValue, bsName.AsStringView()); if (!fxv8::IsNull(newPropertyValue)) fn(pIsolate, newPropertyValue); } return true; } void CFXJSE_FormCalcContext::ApplyToObject( v8::Isolate* pIsolate, std::function)> fn, v8::Local pObject) { v8::Local newPropertyValue = GetObjectDefaultValue(pIsolate, pObject); if (!fxv8::IsNull(newPropertyValue)) fn(pIsolate, newPropertyValue); } void CFXJSE_FormCalcContext::ThrowNoDefaultPropertyException( ByteStringView name) const { ByteString msg(name); msg += " doesn't have a default property."; ThrowException(msg.AsStringView()); } void CFXJSE_FormCalcContext::ThrowCompilerErrorException() const { ThrowException("Compiler error."); } void CFXJSE_FormCalcContext::ThrowDivideByZeroException() const { ThrowException("Divide by zero."); } void CFXJSE_FormCalcContext::ThrowServerDeniedException() const { ThrowException("Server does not permit operation."); } void CFXJSE_FormCalcContext::ThrowPropertyNotInObjectException( ByteStringView name, ByteStringView exp) const { ByteString msg("An attempt was made to reference property '"); msg += name; msg += "' of a non-object in SOM expression "; msg += exp; msg += "."; ThrowException(msg.AsStringView()); } void CFXJSE_FormCalcContext::ThrowParamCountMismatchException( ByteStringView method) const { ByteString msg("Incorrect number of parameters calling method '"); msg += method; msg += "'."; ThrowException(msg.AsStringView()); } void CFXJSE_FormCalcContext::ThrowArgumentMismatchException() const { ThrowException("Argument mismatch in property or function argument."); } void CFXJSE_FormCalcContext::ThrowException(ByteStringView str) const { DCHECK(!str.IsEmpty()); FXJSE_ThrowMessage(GetIsolate(), str); }