// Copyright 2014 PDFium Authors. All rights reserved. // 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 "xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h" #include #include #include #include "core/fxcrt/autorestorer.h" #include "core/fxcrt/cfx_widetextbuf.h" #include "core/fxcrt/fx_extension.h" #include "third_party/base/logging.h" #include "xfa/fxfa/fm2js/cxfa_fmtojavascriptdepth.h" namespace { const wchar_t* const g_BuiltInFuncs[] = { L"Abs", L"Apr", L"At", L"Avg", L"Ceil", L"Choose", L"Concat", L"Count", L"Cterm", L"Date", L"Date2Num", L"DateFmt", L"Decode", L"Encode", L"Eval", L"Exists", L"Floor", L"Format", L"FV", L"Get", L"HasValue", L"If", L"Ipmt", L"IsoDate2Num", L"IsoTime2Num", L"Left", L"Len", L"LocalDateFmt", L"LocalTimeFmt", L"Lower", L"Ltrim", L"Max", L"Min", L"Mod", L"NPV", L"Num2Date", L"Num2GMTime", L"Num2Time", L"Oneof", L"Parse", L"Pmt", L"Post", L"PPmt", L"Put", L"PV", L"Rate", L"Ref", L"Replace", L"Right", L"Round", L"Rtrim", L"Space", L"Str", L"Stuff", L"Substr", L"Sum", L"Term", L"Time", L"Time2Num", L"TimeFmt", L"UnitType", L"UnitValue", L"Upper", L"Uuid", L"Within", L"WordNum", }; const size_t g_BuiltInFuncsMaxLen = 12; struct XFA_FMSOMMethod { const wchar_t* m_wsSomMethodName; uint32_t m_dParameters; }; const XFA_FMSOMMethod gs_FMSomMethods[] = { {L"absPage", 0x01}, {L"absPageInBatch", 0x01}, {L"absPageSpan", 0x01}, {L"append", 0x01}, {L"clear", 0x01}, {L"formNodes", 0x01}, {L"h", 0x01}, {L"insert", 0x03}, {L"isRecordGroup", 0x01}, {L"page", 0x01}, {L"pageSpan", 0x01}, {L"remove", 0x01}, {L"saveFilteredXML", 0x01}, {L"setElement", 0x01}, {L"sheet", 0x01}, {L"sheetInBatch", 0x01}, {L"sign", 0x61}, {L"verify", 0x0d}, {L"w", 0x01}, {L"x", 0x01}, {L"y", 0x01}, }; } // namespace CXFA_FMSimpleExpression::CXFA_FMSimpleExpression(XFA_FM_TOKEN op) : m_op(op) {} XFA_FM_TOKEN CXFA_FMSimpleExpression::GetOperatorToken() const { return m_op; } CXFA_FMNullExpression::CXFA_FMNullExpression() : CXFA_FMSimpleExpression(TOKnull) {} bool CXFA_FMNullExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; js << L"null"; return !CXFA_IsTooBig(js); } CXFA_FMNumberExpression::CXFA_FMNumberExpression(WideStringView wsNumber) : CXFA_FMSimpleExpression(TOKnumber), m_wsNumber(wsNumber) {} CXFA_FMNumberExpression::~CXFA_FMNumberExpression() = default; bool CXFA_FMNumberExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; js << m_wsNumber; return !CXFA_IsTooBig(js); } CXFA_FMStringExpression::CXFA_FMStringExpression(WideStringView wsString) : CXFA_FMSimpleExpression(TOKstring), m_wsString(wsString) {} CXFA_FMStringExpression::~CXFA_FMStringExpression() = default; bool CXFA_FMStringExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; WideString tempStr(m_wsString); if (tempStr.GetLength() <= 2) { js << tempStr; return !CXFA_IsTooBig(js); } js << L"\""; for (size_t i = 1; i < tempStr.GetLength() - 1; i++) { wchar_t oneChar = tempStr[i]; switch (oneChar) { case L'\"': ++i; js << L"\\\""; break; case 0x0d: break; case 0x0a: js << L"\\n"; break; default: js.AppendChar(oneChar); break; } } js << L"\""; return !CXFA_IsTooBig(js); } CXFA_FMIdentifierExpression::CXFA_FMIdentifierExpression( WideStringView wsIdentifier) : CXFA_FMSimpleExpression(TOKidentifier), m_wsIdentifier(wsIdentifier) {} CXFA_FMIdentifierExpression::~CXFA_FMIdentifierExpression() = default; bool CXFA_FMIdentifierExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; if (m_wsIdentifier == L"$") js << L"this"; else if (m_wsIdentifier == L"!") js << L"xfa.datasets"; else if (m_wsIdentifier == L"$data") js << L"xfa.datasets.data"; else if (m_wsIdentifier == L"$event") js << L"xfa.event"; else if (m_wsIdentifier == L"$form") js << L"xfa.form"; else if (m_wsIdentifier == L"$host") js << L"xfa.host"; else if (m_wsIdentifier == L"$layout") js << L"xfa.layout"; else if (m_wsIdentifier == L"$template") js << L"xfa.template"; else if (m_wsIdentifier[0] == L'!') js << L"pfm__excl__" + m_wsIdentifier.Right(m_wsIdentifier.GetLength() - 1); else js << m_wsIdentifier; return !CXFA_IsTooBig(js); } CXFA_FMAssignExpression::CXFA_FMAssignExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMSimpleExpression(op), m_pExp1(std::move(pExp1)), m_pExp2(std::move(pExp2)) {} CXFA_FMAssignExpression::~CXFA_FMAssignExpression() = default; bool CXFA_FMAssignExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; CFX_WideTextBuf tempExp1; if (!m_pExp1->ToJavaScript(tempExp1, ReturnType::kInfered)) return false; js << L"if (pfm_rt.is_obj(" << tempExp1 << L"))\n{\n"; if (type == ReturnType::kImplied) js << L"pfm_ret = "; CFX_WideTextBuf tempExp2; if (!m_pExp2->ToJavaScript(tempExp2, ReturnType::kInfered)) return false; js << L"pfm_rt.asgn_val_op(" << tempExp1 << L", " << tempExp2 << L");\n}\n"; if (m_pExp1->GetOperatorToken() == TOKidentifier && tempExp1.AsStringView() != L"this") { js << L"else\n{\n"; if (type == ReturnType::kImplied) js << L"pfm_ret = "; js << tempExp1 << L" = pfm_rt.asgn_val_op"; js << L"(" << tempExp1 << L", " << tempExp2 << L");\n"; js << L"}\n"; } return !CXFA_IsTooBig(js); } CXFA_FMBinExpression::CXFA_FMBinExpression( const WideString& opName, XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMSimpleExpression(op), m_OpName(opName), m_pExp1(std::move(pExp1)), m_pExp2(std::move(pExp2)) {} CXFA_FMBinExpression::~CXFA_FMBinExpression() = default; bool CXFA_FMBinExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; js << L"pfm_rt." << m_OpName << L"("; if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered)) return false; js << L", "; if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered)) return false; js << L")"; return !CXFA_IsTooBig(js); } CXFA_FMLogicalOrExpression::CXFA_FMLogicalOrExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"log_or_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMLogicalAndExpression::CXFA_FMLogicalAndExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"log_and_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMEqualExpression::CXFA_FMEqualExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"eq_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMNotEqualExpression::CXFA_FMNotEqualExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"neq_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMGtExpression::CXFA_FMGtExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"gt_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMGeExpression::CXFA_FMGeExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"ge_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMLtExpression::CXFA_FMLtExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"lt_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMLeExpression::CXFA_FMLeExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"le_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMPlusExpression::CXFA_FMPlusExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"plus_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMMinusExpression::CXFA_FMMinusExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"minus_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMMulExpression::CXFA_FMMulExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"mul_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMDivExpression::CXFA_FMDivExpression( XFA_FM_TOKEN op, std::unique_ptr pExp1, std::unique_ptr pExp2) : CXFA_FMBinExpression(L"div_op", op, std::move(pExp1), std::move(pExp2)) {} CXFA_FMUnaryExpression::CXFA_FMUnaryExpression( const WideString& opName, XFA_FM_TOKEN op, std::unique_ptr pExp) : CXFA_FMSimpleExpression(op), m_OpName(opName), m_pExp(std::move(pExp)) {} CXFA_FMUnaryExpression::~CXFA_FMUnaryExpression() = default; bool CXFA_FMUnaryExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; js << L"pfm_rt." << m_OpName.c_str() << L"("; if (!m_pExp->ToJavaScript(js, ReturnType::kInfered)) return false; js << L")"; return !CXFA_IsTooBig(js); } CXFA_FMPosExpression::CXFA_FMPosExpression( std::unique_ptr pExp) : CXFA_FMUnaryExpression(L"pos_op", TOKplus, std::move(pExp)) {} CXFA_FMNegExpression::CXFA_FMNegExpression( std::unique_ptr pExp) : CXFA_FMUnaryExpression(L"neg_op", TOKminus, std::move(pExp)) {} CXFA_FMNotExpression::CXFA_FMNotExpression( std::unique_ptr pExp) : CXFA_FMUnaryExpression(L"log_not_op", TOKksnot, std::move(pExp)) {} CXFA_FMCallExpression::CXFA_FMCallExpression( std::unique_ptr pExp, std::vector>&& pArguments, bool bIsSomMethod) : CXFA_FMSimpleExpression(TOKcall), m_pExp(std::move(pExp)), m_bIsSomMethod(bIsSomMethod), m_Arguments(std::move(pArguments)) {} CXFA_FMCallExpression::~CXFA_FMCallExpression() {} bool CXFA_FMCallExpression::IsBuiltInFunc(CFX_WideTextBuf* funcName) { if (funcName->GetLength() > g_BuiltInFuncsMaxLen) return false; WideString str = funcName->MakeString(); const wchar_t* const* pMatchResult = std::lower_bound( std::begin(g_BuiltInFuncs), std::end(g_BuiltInFuncs), str, [](const wchar_t* iter, const WideString& val) -> bool { return val.CompareNoCase(iter) > 0; }); if (pMatchResult != std::end(g_BuiltInFuncs) && !str.CompareNoCase(*pMatchResult)) { funcName->Clear(); *funcName << *pMatchResult; return true; } return false; } uint32_t CXFA_FMCallExpression::IsMethodWithObjParam( const WideString& methodName) { const XFA_FMSOMMethod* result = std::lower_bound( std::begin(gs_FMSomMethods), std::end(gs_FMSomMethods), methodName, [](const XFA_FMSOMMethod iter, const WideString& val) { return val.Compare(iter.m_wsSomMethodName) > 0; }); if (result != std::end(gs_FMSomMethods) && !methodName.Compare(result->m_wsSomMethodName)) { return result->m_dParameters; } return 0; } bool CXFA_FMCallExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; CFX_WideTextBuf funcName; if (!m_pExp->ToJavaScript(funcName, ReturnType::kInfered)) return false; if (m_bIsSomMethod) { js << funcName << L"("; uint32_t methodPara = IsMethodWithObjParam(funcName.MakeString()); if (methodPara > 0) { for (size_t i = 0; i < m_Arguments.size(); ++i) { // Currently none of our expressions use objects for a parameter over // the 6th. Make sure we don't overflow the shift when doing this // check. If we ever need more the 32 object params we can revisit. js << L"pfm_rt.get_"; if (i < 32 && (methodPara & (0x01 << i)) > 0) js << L"jsobj"; else js << L"val"; js << L"("; if (!m_Arguments[i]->ToJavaScript(js, ReturnType::kInfered)) return false; js << L")"; if (i + 1 < m_Arguments.size()) js << L", "; } } else { for (const auto& expr : m_Arguments) { js << L"pfm_rt.get_val("; if (!expr->ToJavaScript(js, ReturnType::kInfered)) return false; js << L")"; if (expr != m_Arguments.back()) js << L", "; } } js << L")"; return !CXFA_IsTooBig(js); } bool isEvalFunc = false; bool isExistsFunc = false; if (!IsBuiltInFunc(&funcName)) { // If a function is not a SomMethod or a built-in then the input was // invalid, so failing. The scanner/lexer should catch this, but currently // doesn't. This failure will bubble up to the top-level and cause the // transpile to fail. return false; } if (funcName.AsStringView() == L"Eval") { isEvalFunc = true; js << L"eval.call(this, pfm_rt.Translate"; } else { if (funcName.AsStringView() == L"Exists") isExistsFunc = true; js << L"pfm_rt." << funcName; } js << L"("; if (isExistsFunc) { js << L"\n(\nfunction ()\n{\ntry\n{\n"; if (!m_Arguments.empty()) { js << L"return "; if (!m_Arguments[0]->ToJavaScript(js, ReturnType::kInfered)) return false; js << L";\n}\n"; } else { js << L"return 0;\n}\n"; } js << L"catch(accessExceptions)\n"; js << L"{\nreturn 0;\n}\n}\n).call(this)\n"; } else { for (const auto& expr : m_Arguments) { if (!expr->ToJavaScript(js, ReturnType::kInfered)) return false; if (expr != m_Arguments.back()) js << L", "; } } js << L")"; if (isEvalFunc) js << L")"; return !CXFA_IsTooBig(js); } CXFA_FMDotAccessorExpression::CXFA_FMDotAccessorExpression( std::unique_ptr pAccessor, XFA_FM_TOKEN op, WideStringView wsIdentifier, std::unique_ptr pIndexExp) : CXFA_FMSimpleExpression(op), m_wsIdentifier(wsIdentifier), m_pExp1(std::move(pAccessor)), m_pExp2(std::move(pIndexExp)) {} CXFA_FMDotAccessorExpression::~CXFA_FMDotAccessorExpression() = default; bool CXFA_FMDotAccessorExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; js << L"pfm_rt.dot_acc("; CFX_WideTextBuf tempExp1; if (m_pExp1) { if (!m_pExp1->ToJavaScript(tempExp1, ReturnType::kInfered)) return false; js << tempExp1; } else { js << L"null"; } js << L", \""; if (m_pExp1 && m_pExp1->GetOperatorToken() == TOKidentifier) js << tempExp1; js << L"\", "; if (m_op == TOKdotscream) js << L"\"#" << m_wsIdentifier << L"\", "; else if (m_op == TOKdotstar) js << L"\"*\", "; else if (m_op == TOKcall) js << L"\"\", "; else js << L"\"" << m_wsIdentifier << L"\", "; if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered)) return false; js << L")"; return !CXFA_IsTooBig(js); } CXFA_FMIndexExpression::CXFA_FMIndexExpression( XFA_FM_AccessorIndex accessorIndex, std::unique_ptr pIndexExp, bool bIsStarIndex) : CXFA_FMSimpleExpression(TOKlbracket), m_pExp(std::move(pIndexExp)), m_accessorIndex(accessorIndex), m_bIsStarIndex(bIsStarIndex) {} CXFA_FMIndexExpression::~CXFA_FMIndexExpression() = default; bool CXFA_FMIndexExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; switch (m_accessorIndex) { case ACCESSOR_NO_INDEX: js << L"0"; break; case ACCESSOR_NO_RELATIVEINDEX: js << L"1"; break; case ACCESSOR_POSITIVE_INDEX: js << L"2"; break; case ACCESSOR_NEGATIVE_INDEX: js << L"3"; break; default: js << L"0"; } if (m_bIsStarIndex) return !CXFA_IsTooBig(js); js << L", "; if (m_pExp) { if (!m_pExp->ToJavaScript(js, ReturnType::kInfered)) return false; } else { js << L"0"; } return !CXFA_IsTooBig(js); } CXFA_FMDotDotAccessorExpression::CXFA_FMDotDotAccessorExpression( std::unique_ptr pAccessor, XFA_FM_TOKEN op, WideStringView wsIdentifier, std::unique_ptr pIndexExp) : CXFA_FMSimpleExpression(op), m_wsIdentifier(wsIdentifier), m_pExp1(std::move(pAccessor)), m_pExp2(std::move(pIndexExp)) {} CXFA_FMDotDotAccessorExpression::~CXFA_FMDotDotAccessorExpression() = default; bool CXFA_FMDotDotAccessorExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; js << L"pfm_rt.dotdot_acc("; if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered)) return false; js << L", " << L"\""; if (m_pExp1->GetOperatorToken() == TOKidentifier) { if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered)) return false; } js << L"\", \"" << m_wsIdentifier << L"\", "; if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered)) return false; js << L")"; return !CXFA_IsTooBig(js); } CXFA_FMMethodCallExpression::CXFA_FMMethodCallExpression( std::unique_ptr pAccessorExp1, std::unique_ptr pCallExp) : CXFA_FMSimpleExpression(TOKdot), m_pExp1(std::move(pAccessorExp1)), m_pExp2(std::move(pCallExp)) {} CXFA_FMMethodCallExpression::~CXFA_FMMethodCallExpression() = default; bool CXFA_FMMethodCallExpression::ToJavaScript(CFX_WideTextBuf& js, ReturnType type) { CXFA_FMToJavaScriptDepth depthManager; if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth()) return false; CFX_WideTextBuf exp2_txt; if (!m_pExp2->ToJavaScript(exp2_txt, ReturnType::kInfered)) return false; js << L"(\nfunction ()\n{\n"; js << L"var method_return_value = null;\n"; js << L"var accessor_object = "; if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered)) return false; js << L";\n"; js << L"if (pfm_rt.is_ary(accessor_object))\n{\n"; js << L"for(var index = accessor_object.length - 1; index > 1; " L"index--)\n{\n"; js << L"method_return_value = accessor_object[index]." << exp2_txt << L";\n"; js << L"}\n}\nelse\n{\n"; js << L"method_return_value = accessor_object." << exp2_txt << L";\n"; js << L"}\n"; js << L"return method_return_value;\n"; js << L"}\n).call(this)"; return !CXFA_IsTooBig(js); } bool CXFA_IsTooBig(const CFX_WideTextBuf& javascript) { return javascript.GetSize() >= 256 * 1024 * 1024; }