blob: 313f0614ff410281b8770a8d0f729704a0d16c1f [file] [log] [blame]
// 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/formcalc/cxfa_fmexpression.h"
#include <algorithm>
#include <utility>
#include "core/fxcrt/autorestorer.h"
#include "core/fxcrt/cfx_widetextbuf.h"
#include "core/fxcrt/fx_extension.h"
#include "fxjs/gc/container_trace.h"
#include "third_party/base/check.h"
#include "v8/include/cppgc/visitor.h"
#include "xfa/fxfa/formcalc/cxfa_fmtojavascriptdepth.h"
namespace {
const wchar_t kLessEqual[] = L" <= ";
const wchar_t kGreaterEqual[] = L" >= ";
const wchar_t kPlusEqual[] = L" += ";
const wchar_t kMinusEqual[] = L" -= ";
const wchar_t* const kBuiltInFuncs[] = {
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 kBuiltInFuncsMaxLen = 12;
struct XFA_FMSOMMethod {
const wchar_t* m_wsSomMethodName; // Ok, POD struct.
uint32_t m_dParameters;
};
const XFA_FMSOMMethod kFMSomMethods[] = {
{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},
};
WideString IdentifierToName(const WideString& ident) {
if (ident.IsEmpty() || ident[0] != L'!')
return ident;
return L"pfm__excl__" + ident.Last(ident.GetLength() - 1);
}
} // namespace
CXFA_FMExpression::CXFA_FMExpression() = default;
CXFA_FMExpression::~CXFA_FMExpression() = default;
void CXFA_FMExpression::Trace(cppgc::Visitor* visitor) const {}
CXFA_FMSimpleExpression::CXFA_FMSimpleExpression(XFA_FM_TOKEN op) : m_op(op) {}
CXFA_FMSimpleExpression::~CXFA_FMSimpleExpression() = default;
CXFA_FMChainableExpression::CXFA_FMChainableExpression(
XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMSimpleExpression(op), m_pExp1(pExp1), m_pExp2(pExp2) {}
CXFA_FMChainableExpression::~CXFA_FMChainableExpression() = default;
void CXFA_FMChainableExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMSimpleExpression::Trace(visitor);
visitor->Trace(m_pExp1);
visitor->Trace(m_pExp2);
}
CXFA_FMNullExpression::CXFA_FMNullExpression()
: CXFA_FMSimpleExpression(TOKnull) {}
CXFA_FMNullExpression::~CXFA_FMNullExpression() = default;
bool CXFA_FMNullExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
*js << "null";
return !CXFA_IsTooBig(*js);
}
CXFA_FMNumberExpression::CXFA_FMNumberExpression(WideString wsNumber)
: CXFA_FMSimpleExpression(TOKnumber), m_wsNumber(std::move(wsNumber)) {}
CXFA_FMNumberExpression::~CXFA_FMNumberExpression() = default;
bool CXFA_FMNumberExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
*js << m_wsNumber;
return !CXFA_IsTooBig(*js);
}
CXFA_FMStringExpression::CXFA_FMStringExpression(WideString wsString)
: CXFA_FMSimpleExpression(TOKstring), m_wsString(std::move(wsString)) {}
CXFA_FMStringExpression::~CXFA_FMStringExpression() = default;
bool CXFA_FMStringExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
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 << "\"";
for (size_t i = 1; i < tempStr.GetLength() - 1; i++) {
wchar_t oneChar = tempStr[i];
switch (oneChar) {
case L'\"':
++i;
*js << "\\\"";
break;
case 0x0d:
break;
case 0x0a:
*js << "\\n";
break;
default:
js->AppendChar(oneChar);
break;
}
}
*js << "\"";
return !CXFA_IsTooBig(*js);
}
CXFA_FMIdentifierExpression::CXFA_FMIdentifierExpression(
WideString wsIdentifier)
: CXFA_FMSimpleExpression(TOKidentifier),
m_wsIdentifier(std::move(wsIdentifier)) {}
CXFA_FMIdentifierExpression::~CXFA_FMIdentifierExpression() = default;
bool CXFA_FMIdentifierExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
if (m_wsIdentifier.EqualsASCII("$"))
*js << "this";
else if (m_wsIdentifier.EqualsASCII("!"))
*js << "xfa.datasets";
else if (m_wsIdentifier.EqualsASCII("$data"))
*js << "xfa.datasets.data";
else if (m_wsIdentifier.EqualsASCII("$event"))
*js << "xfa.event";
else if (m_wsIdentifier.EqualsASCII("$form"))
*js << "xfa.form";
else if (m_wsIdentifier.EqualsASCII("$host"))
*js << "xfa.host";
else if (m_wsIdentifier.EqualsASCII("$layout"))
*js << "xfa.layout";
else if (m_wsIdentifier.EqualsASCII("$template"))
*js << "xfa.template";
else if (m_wsIdentifier[0] == L'!')
*js << "pfm__excl__" << m_wsIdentifier.Last(m_wsIdentifier.GetLength() - 1);
else
*js << m_wsIdentifier;
return !CXFA_IsTooBig(*js);
}
CXFA_FMAssignExpression::CXFA_FMAssignExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMChainableExpression(op, pExp1, pExp2) {}
CXFA_FMAssignExpression::~CXFA_FMAssignExpression() = default;
bool CXFA_FMAssignExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
CFX_WideTextBuf tempExp1;
const CXFA_FMSimpleExpression* exp1 = GetFirstExpression();
if (!exp1->ToJavaScript(&tempExp1, ReturnType::kInferred))
return false;
*js << "if (pfm_rt.is_obj(" << tempExp1 << "))\n{\n";
if (type == ReturnType::kImplied)
*js << "pfm_ret = ";
CFX_WideTextBuf tempExp2;
const CXFA_FMSimpleExpression* exp2 = GetSecondExpression();
if (!exp2->ToJavaScript(&tempExp2, ReturnType::kInferred))
return false;
*js << "pfm_rt.asgn_val_op(" << tempExp1 << ", " << tempExp2 << ");\n}\n";
if (exp1->GetOperatorToken() == TOKidentifier &&
!tempExp1.AsStringView().EqualsASCII("this")) {
*js << "else\n{\n";
if (type == ReturnType::kImplied)
*js << "pfm_ret = ";
*js << tempExp1 << " = pfm_rt.asgn_val_op";
*js << "(" << tempExp1 << ", " << tempExp2 << ");\n";
*js << "}\n";
}
return !CXFA_IsTooBig(*js);
}
CXFA_FMBinExpression::CXFA_FMBinExpression(const WideString& opName,
XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMChainableExpression(op, pExp1, pExp2), m_OpName(opName) {}
CXFA_FMBinExpression::~CXFA_FMBinExpression() = default;
bool CXFA_FMBinExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
*js << "pfm_rt." << m_OpName << "(";
if (!GetFirstExpression()->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ", ";
if (!GetSecondExpression()->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ")";
return !CXFA_IsTooBig(*js);
}
CXFA_FMLogicalOrExpression::CXFA_FMLogicalOrExpression(
XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"log_or_op", op, pExp1, pExp2) {}
CXFA_FMLogicalOrExpression::~CXFA_FMLogicalOrExpression() = default;
CXFA_FMLogicalAndExpression::CXFA_FMLogicalAndExpression(
XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"log_and_op", op, pExp1, pExp2) {}
CXFA_FMLogicalAndExpression::~CXFA_FMLogicalAndExpression() = default;
CXFA_FMEqualExpression::CXFA_FMEqualExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"eq_op", op, pExp1, pExp2) {}
CXFA_FMEqualExpression::~CXFA_FMEqualExpression() = default;
CXFA_FMNotEqualExpression::CXFA_FMNotEqualExpression(
XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"neq_op", op, pExp1, pExp2) {}
CXFA_FMNotEqualExpression::~CXFA_FMNotEqualExpression() = default;
CXFA_FMGtExpression::CXFA_FMGtExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"gt_op", op, pExp1, pExp2) {}
CXFA_FMGtExpression::~CXFA_FMGtExpression() = default;
CXFA_FMGeExpression::CXFA_FMGeExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"ge_op", op, pExp1, pExp2) {}
CXFA_FMGeExpression::~CXFA_FMGeExpression() = default;
CXFA_FMLtExpression::CXFA_FMLtExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"lt_op", op, pExp1, pExp2) {}
CXFA_FMLtExpression::~CXFA_FMLtExpression() = default;
CXFA_FMLeExpression::CXFA_FMLeExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"le_op", op, pExp1, pExp2) {}
CXFA_FMLeExpression::~CXFA_FMLeExpression() = default;
CXFA_FMPlusExpression::CXFA_FMPlusExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"plus_op", op, pExp1, pExp2) {}
CXFA_FMPlusExpression::~CXFA_FMPlusExpression() = default;
CXFA_FMMinusExpression::CXFA_FMMinusExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"minus_op", op, pExp1, pExp2) {}
CXFA_FMMinusExpression::~CXFA_FMMinusExpression() = default;
CXFA_FMMulExpression::CXFA_FMMulExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"mul_op", op, pExp1, pExp2) {}
CXFA_FMMulExpression::~CXFA_FMMulExpression() = default;
CXFA_FMDivExpression::CXFA_FMDivExpression(XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp1,
CXFA_FMSimpleExpression* pExp2)
: CXFA_FMBinExpression(L"div_op", op, pExp1, pExp2) {}
CXFA_FMDivExpression::~CXFA_FMDivExpression() = default;
CXFA_FMUnaryExpression::CXFA_FMUnaryExpression(const WideString& opName,
XFA_FM_TOKEN op,
CXFA_FMSimpleExpression* pExp)
: CXFA_FMSimpleExpression(op), m_OpName(opName), m_pExp(pExp) {}
CXFA_FMUnaryExpression::~CXFA_FMUnaryExpression() = default;
void CXFA_FMUnaryExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMSimpleExpression::Trace(visitor);
visitor->Trace(m_pExp);
}
bool CXFA_FMUnaryExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
*js << "pfm_rt." << m_OpName << "(";
if (!m_pExp->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ")";
return !CXFA_IsTooBig(*js);
}
CXFA_FMPosExpression::CXFA_FMPosExpression(CXFA_FMSimpleExpression* pExp)
: CXFA_FMUnaryExpression(L"pos_op", TOKplus, pExp) {}
CXFA_FMPosExpression::~CXFA_FMPosExpression() = default;
CXFA_FMNegExpression::CXFA_FMNegExpression(CXFA_FMSimpleExpression* pExp)
: CXFA_FMUnaryExpression(L"neg_op", TOKminus, pExp) {}
CXFA_FMNegExpression::~CXFA_FMNegExpression() = default;
CXFA_FMNotExpression::CXFA_FMNotExpression(CXFA_FMSimpleExpression* pExp)
: CXFA_FMUnaryExpression(L"log_not_op", TOKksnot, pExp) {}
CXFA_FMNotExpression::~CXFA_FMNotExpression() = default;
CXFA_FMCallExpression::CXFA_FMCallExpression(
CXFA_FMSimpleExpression* pExp,
std::vector<cppgc::Member<CXFA_FMSimpleExpression>>&& pArguments,
bool bIsSomMethod)
: CXFA_FMSimpleExpression(TOKcall),
m_pExp(pExp),
m_Arguments(std::move(pArguments)),
m_bIsSomMethod(bIsSomMethod) {}
CXFA_FMCallExpression::~CXFA_FMCallExpression() = default;
void CXFA_FMCallExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMSimpleExpression::Trace(visitor);
visitor->Trace(m_pExp);
ContainerTrace(visitor, m_Arguments);
}
bool CXFA_FMCallExpression::IsBuiltInFunc(CFX_WideTextBuf* funcName) const {
if (funcName->GetLength() > kBuiltInFuncsMaxLen)
return false;
WideString str = funcName->MakeString();
const wchar_t* const* pMatchResult =
std::lower_bound(std::begin(kBuiltInFuncs), std::end(kBuiltInFuncs), str,
[](const wchar_t* iter, const WideString& val) -> bool {
return val.CompareNoCase(iter) > 0;
});
if (pMatchResult != std::end(kBuiltInFuncs) &&
!str.CompareNoCase(*pMatchResult)) {
funcName->Clear();
*funcName << *pMatchResult;
return true;
}
return false;
}
uint32_t CXFA_FMCallExpression::IsMethodWithObjParam(
const WideString& methodName) const {
const XFA_FMSOMMethod* result = std::lower_bound(
std::begin(kFMSomMethods), std::end(kFMSomMethods), methodName,
[](const XFA_FMSOMMethod iter, const WideString& val) {
return val.Compare(iter.m_wsSomMethodName) > 0;
});
if (result != std::end(kFMSomMethods) &&
methodName == result->m_wsSomMethodName) {
return result->m_dParameters;
}
return 0;
}
bool CXFA_FMCallExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
CFX_WideTextBuf funcName;
if (!m_pExp->ToJavaScript(&funcName, ReturnType::kInferred))
return false;
if (m_bIsSomMethod) {
*js << funcName << "(";
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 << "pfm_rt.get_";
if (i < 32 && (methodPara & (0x01 << i)) > 0)
*js << "jsobj";
else
*js << "val";
*js << "(";
if (!m_Arguments[i]->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ")";
if (i + 1 < m_Arguments.size())
*js << ", ";
}
} else {
for (const auto& expr : m_Arguments) {
*js << "pfm_rt.get_val(";
if (!expr->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ")";
if (expr != m_Arguments.back())
*js << ", ";
}
}
*js << ")";
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().EqualsASCII("Eval")) {
isEvalFunc = true;
*js << "eval.call(this, pfm_rt.Translate";
} else {
if (funcName.AsStringView().EqualsASCII("Exists"))
isExistsFunc = true;
*js << "pfm_rt." << funcName;
}
*js << "(";
if (isExistsFunc) {
*js << "\n(\nfunction ()\n{\ntry\n{\n";
if (!m_Arguments.empty()) {
*js << "return ";
if (!m_Arguments[0]->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ";\n}\n";
} else {
*js << "return 0;\n}\n";
}
*js << "catch(accessExceptions)\n";
*js << "{\nreturn 0;\n}\n}\n).call(this)\n";
} else {
for (const auto& expr : m_Arguments) {
if (!expr->ToJavaScript(js, ReturnType::kInferred))
return false;
if (expr != m_Arguments.back())
*js << ", ";
}
}
*js << ")";
if (isEvalFunc)
*js << ")";
return !CXFA_IsTooBig(*js);
}
CXFA_FMDotAccessorExpression::CXFA_FMDotAccessorExpression(
CXFA_FMSimpleExpression* pAccessor,
XFA_FM_TOKEN op,
WideString wsIdentifier,
CXFA_FMSimpleExpression* pIndexExp)
: CXFA_FMChainableExpression(op, pAccessor, pIndexExp),
m_wsIdentifier(std::move(wsIdentifier)) {}
CXFA_FMDotAccessorExpression::~CXFA_FMDotAccessorExpression() = default;
bool CXFA_FMDotAccessorExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
*js << "pfm_rt.dot_acc(";
CXFA_FMSimpleExpression* exp1 = GetFirstExpression();
if (exp1) {
// Write directly to the buffer with each recursion. Creating
// and copying temporaries here becomes expensive when there
// is deep recursion, even though we may need to re-create the
// same thing again below. See https://crbug.com/1274018.
if (!exp1->ToJavaScript(js, ReturnType::kInferred))
return false;
} else {
*js << "null";
}
*js << ", \"";
if (exp1 && exp1->GetOperatorToken() == TOKidentifier) {
if (!exp1->ToJavaScript(js, ReturnType::kInferred))
return false;
}
*js << "\", ";
if (GetOperatorToken() == TOKdotscream)
*js << "\"#" << m_wsIdentifier << "\", ";
else if (GetOperatorToken() == TOKdotstar)
*js << "\"*\", ";
else if (GetOperatorToken() == TOKcall)
*js << "\"\", ";
else
*js << "\"" << m_wsIdentifier << "\", ";
CXFA_FMSimpleExpression* exp2 = GetSecondExpression();
if (!exp2->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ")";
return !CXFA_IsTooBig(*js);
}
CXFA_FMIndexExpression::CXFA_FMIndexExpression(
AccessorIndex accessorIndex,
CXFA_FMSimpleExpression* pIndexExp,
bool bIsStarIndex)
: CXFA_FMSimpleExpression(TOKlbracket),
m_pExp(pIndexExp),
m_accessorIndex(accessorIndex),
m_bIsStarIndex(bIsStarIndex) {}
CXFA_FMIndexExpression::~CXFA_FMIndexExpression() = default;
void CXFA_FMIndexExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMSimpleExpression::Trace(visitor);
visitor->Trace(m_pExp);
}
bool CXFA_FMIndexExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
switch (m_accessorIndex) {
case AccessorIndex::kNoIndex:
*js << "0";
break;
case AccessorIndex::kNoRelativeIndex:
*js << "1";
break;
case AccessorIndex::kPositiveIndex:
*js << "2";
break;
case AccessorIndex::kNegativeIndex:
*js << "3";
break;
}
if (m_bIsStarIndex)
return !CXFA_IsTooBig(*js);
*js << ", ";
if (m_pExp) {
if (!m_pExp->ToJavaScript(js, ReturnType::kInferred))
return false;
} else {
*js << "0";
}
return !CXFA_IsTooBig(*js);
}
CXFA_FMDotDotAccessorExpression::CXFA_FMDotDotAccessorExpression(
CXFA_FMSimpleExpression* pAccessor,
XFA_FM_TOKEN op,
WideString wsIdentifier,
CXFA_FMSimpleExpression* pIndexExp)
: CXFA_FMChainableExpression(op, pAccessor, pIndexExp),
m_wsIdentifier(std::move(wsIdentifier)) {}
CXFA_FMDotDotAccessorExpression::~CXFA_FMDotDotAccessorExpression() = default;
bool CXFA_FMDotDotAccessorExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
CXFA_FMSimpleExpression* exp1 = GetFirstExpression();
*js << "pfm_rt.dotdot_acc(";
if (!exp1->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ", "
<< "\"";
if (exp1->GetOperatorToken() == TOKidentifier) {
if (!exp1->ToJavaScript(js, ReturnType::kInferred))
return false;
}
CXFA_FMSimpleExpression* exp2 = GetSecondExpression();
*js << "\", \"" << m_wsIdentifier << "\", ";
if (!exp2->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ")";
return !CXFA_IsTooBig(*js);
}
CXFA_FMMethodCallExpression::CXFA_FMMethodCallExpression(
CXFA_FMSimpleExpression* pAccessorExp1,
CXFA_FMSimpleExpression* pCallExp)
: CXFA_FMChainableExpression(TOKdot, pAccessorExp1, pCallExp) {}
CXFA_FMMethodCallExpression::~CXFA_FMMethodCallExpression() = default;
bool CXFA_FMMethodCallExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
*js << "(function() {\n";
*js << " return pfm_method_runner(";
if (!GetFirstExpression()->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ", function(obj) {\n";
*js << " return obj.";
if (!GetSecondExpression()->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ";\n";
*js << " });\n";
*js << "}).call(this)";
return !CXFA_IsTooBig(*js);
}
CXFA_FMFunctionDefinition::CXFA_FMFunctionDefinition(
WideString wsName,
std::vector<WideString>&& arguments,
std::vector<cppgc::Member<CXFA_FMExpression>>&& expressions)
: m_wsName(std::move(wsName)),
m_pArguments(std::move(arguments)),
m_pExpressions(std::move(expressions)) {
DCHECK(!m_wsName.IsEmpty());
}
CXFA_FMFunctionDefinition::~CXFA_FMFunctionDefinition() = default;
void CXFA_FMFunctionDefinition::Trace(cppgc::Visitor* visitor) const {
CXFA_FMExpression::Trace(visitor);
ContainerTrace(visitor, m_pExpressions);
}
bool CXFA_FMFunctionDefinition::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
if (m_wsName.IsEmpty())
return false;
*js << "function " << IdentifierToName(m_wsName) << "(";
for (const auto& identifier : m_pArguments) {
if (identifier != m_pArguments.front())
*js << ", ";
*js << IdentifierToName(identifier);
}
*js << ") {\n";
*js << "var pfm_ret = null;\n";
for (const auto& expr : m_pExpressions) {
ReturnType ret_type = expr == m_pExpressions.back() ? ReturnType::kImplied
: ReturnType::kInferred;
if (!expr->ToJavaScript(js, ret_type))
return false;
}
*js << "return pfm_ret;\n";
*js << "}\n";
return !CXFA_IsTooBig(*js);
}
CXFA_FMAST::CXFA_FMAST(
std::vector<cppgc::Member<CXFA_FMExpression>> expressions)
: expressions_(std::move(expressions)) {}
CXFA_FMAST::~CXFA_FMAST() = default;
void CXFA_FMAST::Trace(cppgc::Visitor* visitor) const {
ContainerTrace(visitor, expressions_);
}
absl::optional<CFX_WideTextBuf> CXFA_FMAST::ToJavaScript() const {
CFX_WideTextBuf js;
if (expressions_.empty()) {
js << "// comments only";
return js;
}
js << "(function() {\n";
js << "let pfm_method_runner = function(obj, cb) {\n";
js << " if (pfm_rt.is_ary(obj)) {\n";
js << " let pfm_method_return = null;\n";
js << " for (var idx = obj.length -1; idx > 1; idx--) {\n";
js << " pfm_method_return = cb(obj[idx]);\n";
js << " }\n";
js << " return pfm_method_return;\n";
js << " }\n";
js << " return cb(obj);\n";
js << "};\n";
js << "var pfm_ret = null;\n";
for (const auto& expr : expressions_) {
CXFA_FMAssignExpression::ReturnType ret_type =
expr == expressions_.back()
? CXFA_FMAssignExpression::ReturnType::kImplied
: CXFA_FMAssignExpression::ReturnType::kInferred;
if (!expr->ToJavaScript(&js, ret_type))
return absl::nullopt;
}
js << "return pfm_rt.get_val(pfm_ret);\n";
js << "}).call(this);";
if (CXFA_IsTooBig(js))
return absl::nullopt;
return js;
}
CXFA_FMVarExpression::CXFA_FMVarExpression(WideString wsName,
CXFA_FMSimpleExpression* pInit)
: m_wsName(std::move(wsName)), m_pInit(pInit) {}
CXFA_FMVarExpression::~CXFA_FMVarExpression() = default;
void CXFA_FMVarExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMExpression::Trace(visitor);
visitor->Trace(m_pInit);
}
bool CXFA_FMVarExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
WideString tempName = IdentifierToName(m_wsName);
*js << "var " << tempName << " = ";
if (m_pInit) {
if (!m_pInit->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ";\n";
*js << tempName << " = pfm_rt.var_filter(" << tempName << ");\n";
} else {
*js << "\"\";\n";
}
if (type == ReturnType::kImplied)
*js << "pfm_ret = " << tempName << ";\n";
return !CXFA_IsTooBig(*js);
}
CXFA_FMExpExpression::CXFA_FMExpExpression(CXFA_FMSimpleExpression* pExpression)
: m_pExpression(pExpression) {}
CXFA_FMExpExpression::~CXFA_FMExpExpression() = default;
void CXFA_FMExpExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMExpression::Trace(visitor);
visitor->Trace(m_pExpression);
}
bool CXFA_FMExpExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
if (type == ReturnType::kInferred) {
bool ret = m_pExpression->ToJavaScript(js, ReturnType::kInferred);
if (m_pExpression->GetOperatorToken() != TOKassign)
*js << ";\n";
return ret;
}
if (m_pExpression->GetOperatorToken() == TOKassign)
return m_pExpression->ToJavaScript(js, ReturnType::kImplied);
if (m_pExpression->GetOperatorToken() == TOKstar ||
m_pExpression->GetOperatorToken() == TOKdotstar ||
m_pExpression->GetOperatorToken() == TOKdotscream ||
m_pExpression->GetOperatorToken() == TOKdotdot ||
m_pExpression->GetOperatorToken() == TOKdot) {
*js << "pfm_ret = pfm_rt.get_val(";
if (!m_pExpression->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ");\n";
return !CXFA_IsTooBig(*js);
}
*js << "pfm_ret = ";
if (!m_pExpression->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ";\n";
return !CXFA_IsTooBig(*js);
}
CXFA_FMBlockExpression::CXFA_FMBlockExpression(
std::vector<cppgc::Member<CXFA_FMExpression>>&& pExpressionList)
: m_ExpressionList(std::move(pExpressionList)) {}
CXFA_FMBlockExpression::~CXFA_FMBlockExpression() = default;
void CXFA_FMBlockExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMExpression::Trace(visitor);
ContainerTrace(visitor, m_ExpressionList);
}
bool CXFA_FMBlockExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
*js << "{\n";
for (const auto& expr : m_ExpressionList) {
if (type == ReturnType::kInferred) {
if (!expr->ToJavaScript(js, ReturnType::kInferred))
return false;
} else {
ReturnType ret_type = expr == m_ExpressionList.back()
? ReturnType::kImplied
: ReturnType::kInferred;
if (!expr->ToJavaScript(js, ret_type))
return false;
}
}
*js << "}\n";
return !CXFA_IsTooBig(*js);
}
CXFA_FMDoExpression::CXFA_FMDoExpression(CXFA_FMExpression* pList)
: m_pList(pList) {}
CXFA_FMDoExpression::~CXFA_FMDoExpression() = default;
void CXFA_FMDoExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMExpression::Trace(visitor);
visitor->Trace(m_pList);
}
bool CXFA_FMDoExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
return m_pList->ToJavaScript(js, type);
}
CXFA_FMIfExpression::CXFA_FMIfExpression(
CXFA_FMSimpleExpression* pExpression,
CXFA_FMExpression* pIfExpression,
std::vector<cppgc::Member<CXFA_FMIfExpression>>&& pElseIfExpressions,
CXFA_FMExpression* pElseExpression)
: m_pExpression(pExpression),
m_pIfExpression(pIfExpression),
m_pElseIfExpressions(std::move(pElseIfExpressions)),
m_pElseExpression(pElseExpression) {
DCHECK(m_pExpression);
}
CXFA_FMIfExpression::~CXFA_FMIfExpression() = default;
void CXFA_FMIfExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMExpression::Trace(visitor);
visitor->Trace(m_pExpression);
visitor->Trace(m_pIfExpression);
ContainerTrace(visitor, m_pElseIfExpressions);
visitor->Trace(m_pElseExpression);
}
bool CXFA_FMIfExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
if (type == ReturnType::kImplied)
*js << "pfm_ret = 0;\n";
*js << "if (pfm_rt.get_val(";
if (!m_pExpression->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << "))\n";
if (CXFA_IsTooBig(*js))
return false;
if (m_pIfExpression) {
if (!m_pIfExpression->ToJavaScript(js, type))
return false;
if (CXFA_IsTooBig(*js))
return false;
}
for (auto& expr : m_pElseIfExpressions) {
*js << "else ";
if (!expr->ToJavaScript(js, ReturnType::kInferred))
return false;
}
if (m_pElseExpression) {
*js << "else ";
if (!m_pElseExpression->ToJavaScript(js, type))
return false;
}
return !CXFA_IsTooBig(*js);
}
CXFA_FMWhileExpression::CXFA_FMWhileExpression(
CXFA_FMSimpleExpression* pCondition,
CXFA_FMExpression* pExpression)
: m_pCondition(pCondition), m_pExpression(pExpression) {}
CXFA_FMWhileExpression::~CXFA_FMWhileExpression() = default;
void CXFA_FMWhileExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMExpression::Trace(visitor);
visitor->Trace(m_pCondition);
visitor->Trace(m_pExpression);
}
bool CXFA_FMWhileExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
if (type == ReturnType::kImplied)
*js << "pfm_ret = 0;\n";
*js << "while (";
if (!m_pCondition->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ")\n";
if (CXFA_IsTooBig(*js))
return false;
if (!m_pExpression->ToJavaScript(js, type))
return false;
return !CXFA_IsTooBig(*js);
}
CXFA_FMBreakExpression::CXFA_FMBreakExpression() : CXFA_FMExpression() {}
CXFA_FMBreakExpression::~CXFA_FMBreakExpression() = default;
bool CXFA_FMBreakExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
*js << "pfm_ret = 0;\nbreak;\n";
return !CXFA_IsTooBig(*js);
}
CXFA_FMContinueExpression::CXFA_FMContinueExpression() : CXFA_FMExpression() {}
CXFA_FMContinueExpression::~CXFA_FMContinueExpression() = default;
bool CXFA_FMContinueExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
*js << "pfm_ret = 0;\ncontinue;\n";
return !CXFA_IsTooBig(*js);
}
CXFA_FMForExpression::CXFA_FMForExpression(WideString wsVariant,
CXFA_FMSimpleExpression* pAssignment,
CXFA_FMSimpleExpression* pAccessor,
int32_t iDirection,
CXFA_FMSimpleExpression* pStep,
CXFA_FMExpression* pList)
: m_wsVariant(std::move(wsVariant)),
m_bDirection(iDirection == 1),
m_pAssignment(pAssignment),
m_pAccessor(pAccessor),
m_pStep(pStep),
m_pList(pList) {}
CXFA_FMForExpression::~CXFA_FMForExpression() = default;
void CXFA_FMForExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMExpression::Trace(visitor);
visitor->Trace(m_pAssignment);
visitor->Trace(m_pAccessor);
visitor->Trace(m_pStep);
visitor->Trace(m_pList);
}
bool CXFA_FMForExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
if (type == ReturnType::kImplied)
*js << "pfm_ret = 0;\n";
*js << "{\n";
WideString tmpName = IdentifierToName(m_wsVariant);
*js << "var " << tmpName << " = null;\n";
*js << "for (" << tmpName << " = pfm_rt.get_val(";
if (!m_pAssignment->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << "); ";
*js << tmpName << (m_bDirection ? kLessEqual : kGreaterEqual);
*js << "pfm_rt.get_val(";
if (!m_pAccessor->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << "); ";
*js << tmpName << (m_bDirection ? kPlusEqual : kMinusEqual);
if (m_pStep) {
*js << "pfm_rt.get_val(";
if (!m_pStep->ToJavaScript(js, ReturnType::kInferred))
return false;
*js << ")";
} else {
*js << "1";
}
*js << ")\n";
if (CXFA_IsTooBig(*js))
return false;
if (!m_pList->ToJavaScript(js, type))
return false;
*js << "}\n";
return !CXFA_IsTooBig(*js);
}
CXFA_FMForeachExpression::CXFA_FMForeachExpression(
WideString wsIdentifier,
std::vector<cppgc::Member<CXFA_FMSimpleExpression>>&& pAccessors,
CXFA_FMExpression* pList)
: m_wsIdentifier(std::move(wsIdentifier)),
m_pAccessors(std::move(pAccessors)),
m_pList(pList) {}
CXFA_FMForeachExpression::~CXFA_FMForeachExpression() = default;
void CXFA_FMForeachExpression::Trace(cppgc::Visitor* visitor) const {
CXFA_FMExpression::Trace(visitor);
ContainerTrace(visitor, m_pAccessors);
visitor->Trace(m_pList);
}
bool CXFA_FMForeachExpression::ToJavaScript(CFX_WideTextBuf* js,
ReturnType type) const {
CXFA_FMToJavaScriptDepth depthManager;
if (CXFA_IsTooBig(*js) || !depthManager.IsWithinMaxDepth())
return false;
if (type == ReturnType::kImplied)
*js << "pfm_ret = 0;\n";
*js << "{\n";
WideString tmpName = IdentifierToName(m_wsIdentifier);
*js << "var " << tmpName << " = null;\n";
*js << "var pfm_ary = pfm_rt.concat_obj(";
for (const auto& expr : m_pAccessors) {
if (!expr->ToJavaScript(js, ReturnType::kInferred))
return false;
if (expr != m_pAccessors.back())
*js << ", ";
}
*js << ");\n";
*js << "var pfm_ary_idx = 0;\n";
*js << "while(pfm_ary_idx < pfm_ary.length)\n{\n";
*js << tmpName << " = pfm_ary[pfm_ary_idx++];\n";
if (!m_pList->ToJavaScript(js, type))
return false;
*js << "}\n"; // while
*js << "}\n"; // block
return !CXFA_IsTooBig(*js);
}
bool CXFA_IsTooBig(const CFX_WideTextBuf& js) {
return js.GetSize() >= 256 * 1024 * 1024;
}