RetroZilla/extensions/transformiix/source/xslt/txExecutionState.cpp

615 lines
17 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is TransforMiiX XSLT processor code.
*
* The Initial Developer of the Original Code is
* Jonas Sicking.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jonas Sicking <jonas@sicking.cc>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "txExecutionState.h"
#include "txSingleNodeContext.h"
#include "txInstructions.h"
#include "txStylesheet.h"
#include "txVariableMap.h"
#include "txRtfHandler.h"
#include "txXSLTProcessor.h"
#include "TxLog.h"
#include "txURIUtils.h"
#include "txXMLParser.h"
const PRInt32 txExecutionState::kMaxRecursionDepth = 20000;
nsresult txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
{
nsresult rv = Init(8);
NS_ENSURE_SUCCESS(rv, rv);
mSourceDocument = aSourceDocument;
nsAutoString baseURI;
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
txLoadedDocumentEntry* entry = PutEntry(baseURI);
if (!entry) {
return NS_ERROR_FAILURE;
}
entry->mDocument = mSourceDocument;
return NS_OK;
}
txLoadedDocumentsHash::~txLoadedDocumentsHash()
{
if (!IsInitialized()) {
return;
}
nsAutoString baseURI;
txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
txLoadedDocumentEntry* entry = GetEntry(baseURI);
if (entry) {
delete entry->mDocument.forget();
}
}
txExecutionState::txExecutionState(txStylesheet* aStylesheet,
PRBool aDisableLoads)
: mStylesheet(aStylesheet),
mNextInstruction(nsnull),
mLocalVariables(nsnull),
mRecursionDepth(0),
mTemplateRules(nsnull),
mTemplateRulesBufferSize(0),
mTemplateRuleCount(0),
mEvalContext(nsnull),
mInitialEvalContext(nsnull),
// mRTFDocument(nsnull),
mGlobalParams(nsnull),
mKeyHash(aStylesheet->getKeyMap()),
mDisableLoads(aDisableLoads)
{
MOZ_COUNT_CTOR(txExecutionState);
}
txExecutionState::~txExecutionState()
{
MOZ_COUNT_DTOR(txExecutionState);
delete mResultHandler;
delete mLocalVariables;
delete mEvalContext;
// delete mRTFDocument;
PRInt32 i;
for (i = 0; i < mTemplateRuleCount; ++i) {
NS_IF_RELEASE(mTemplateRules[i].mModeLocalName);
}
delete [] mTemplateRules;
txStackIterator varsIter(&mLocalVarsStack);
while (varsIter.hasNext()) {
delete (txVariableMap*)varsIter.next();
}
txStackIterator contextIter(&mEvalContextStack);
while (contextIter.hasNext()) {
txIEvalContext* context = (txIEvalContext*)contextIter.next();
if (context != mInitialEvalContext) {
delete context;
}
}
txStackIterator handlerIter(&mResultHandlerStack);
while (handlerIter.hasNext()) {
delete (txAXMLEventHandler*)handlerIter.next();
}
txStackIterator paramIter(&mParamStack);
while (paramIter.hasNext()) {
delete (txExpandedNameMap*)paramIter.next();
}
}
nsresult
txExecutionState::init(const txXPathNode& aNode,
txExpandedNameMap* aGlobalParams)
{
nsresult rv = NS_OK;
mGlobalParams = aGlobalParams;
// Set up initial context
mEvalContext = new txSingleNodeContext(aNode, this);
NS_ENSURE_TRUE(mEvalContext, NS_ERROR_OUT_OF_MEMORY);
mInitialEvalContext = mEvalContext;
// Set up output and result-handler
txAXMLEventHandler* handler = 0;
rv = mOutputHandlerFactory->
createHandlerWith(mStylesheet->getOutputFormat(), &handler);
NS_ENSURE_SUCCESS(rv, rv);
mOutputHandler = handler;
mResultHandler = handler;
mOutputHandler->startDocument();
// Set up loaded-documents-hash
nsAutoPtr<txXPathNode> document(txXPathNodeUtils::getOwnerDocument(aNode));
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
rv = mLoadedDocuments.init(document);
NS_ENSURE_SUCCESS(rv, rv);
// loaded-documents-hash owns this now
document.forget();
// Init members
rv = mKeyHash.init();
NS_ENSURE_SUCCESS(rv, rv);
mRecycler = new txResultRecycler;
NS_ENSURE_TRUE(mRecycler, NS_ERROR_OUT_OF_MEMORY);
rv = mRecycler->init();
NS_ENSURE_SUCCESS(rv, rv);
// The actual value here doesn't really matter since noone should use this
// value. But lets put something errorlike in just in case
mGlobalVarPlaceholderValue = new StringResult(NS_LITERAL_STRING("Error"), nsnull);
NS_ENSURE_TRUE(mGlobalVarPlaceholderValue, NS_ERROR_OUT_OF_MEMORY);
// Initiate first instruction. This has to be done last since findTemplate
// might use us.
txStylesheet::ImportFrame* frame = 0;
txExpandedName nullName;
txInstruction* templ = mStylesheet->findTemplate(aNode, nullName,
this, nsnull, &frame);
rv = pushTemplateRule(frame, nullName, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
return runTemplate(templ);
}
nsresult
txExecutionState::end(nsresult aResult)
{
NS_ASSERTION(NS_FAILED(aResult) || mTemplateRuleCount == 1,
"Didn't clean up template rules properly");
if (NS_SUCCEEDED(aResult)) {
popTemplateRule();
}
mOutputHandler->endDocument(aResult);
return NS_OK;
}
nsresult
txExecutionState::getVariable(PRInt32 aNamespace, nsIAtom* aLName,
txAExprResult*& aResult)
{
nsresult rv = NS_OK;
txExpandedName name(aNamespace, aLName);
// look for a local variable
if (mLocalVariables) {
mLocalVariables->getVariable(name, &aResult);
if (aResult) {
return NS_OK;
}
}
// look for an evaluated global variable
mGlobalVariableValues.getVariable(name, &aResult);
if (aResult) {
if (aResult == mGlobalVarPlaceholderValue) {
// XXX ErrorReport: cyclic variable-value
NS_RELEASE(aResult);
return NS_ERROR_XSLT_BAD_RECURSION;
}
return NS_OK;
}
// Is there perchance a global variable not evaluated yet?
txStylesheet::GlobalVariable* var = mStylesheet->getGlobalVariable(name);
if (!var) {
// XXX ErrorReport: variable doesn't exist in this scope
return NS_ERROR_FAILURE;
}
NS_ASSERTION(var->mExpr && !var->mFirstInstruction ||
!var->mExpr && var->mFirstInstruction,
"global variable should have either instruction or expression");
// Is this a stylesheet parameter that has a value?
if (var->mIsParam && mGlobalParams) {
txIGlobalParameter* param =
(txIGlobalParameter*)mGlobalParams->get(name);
if (param) {
rv = param->getValue(&aResult);
NS_ENSURE_SUCCESS(rv, rv);
rv = mGlobalVariableValues.bindVariable(name, aResult);
if (NS_FAILED(rv)) {
NS_RELEASE(aResult);
return rv;
}
return NS_OK;
}
}
// Insert a placeholdervalue to protect against recursion
rv = mGlobalVariableValues.bindVariable(name, mGlobalVarPlaceholderValue);
NS_ENSURE_SUCCESS(rv, rv);
// evaluate the global variable
pushEvalContext(mInitialEvalContext);
if (var->mExpr) {
txVariableMap* oldVars = mLocalVariables;
mLocalVariables = nsnull;
rv = var->mExpr->evaluate(getEvalContext(), &aResult);
mLocalVariables = oldVars;
NS_ENSURE_SUCCESS(rv, rv);
}
else {
nsAutoPtr<txRtfHandler> rtfHandler(new txRtfHandler);
NS_ENSURE_TRUE(rtfHandler, NS_ERROR_OUT_OF_MEMORY);
rv = pushResultHandler(rtfHandler);
NS_ENSURE_SUCCESS(rv, rv);
rtfHandler.forget();
txInstruction* prevInstr = mNextInstruction;
// set return to nsnull to stop execution
mNextInstruction = nsnull;
rv = runTemplate(var->mFirstInstruction);
NS_ENSURE_SUCCESS(rv, rv);
rv = pushTemplateRule(nsnull, txExpandedName(), nsnull);
NS_ENSURE_SUCCESS(rv, rv);
rv = txXSLTProcessor::execute(*this);
NS_ENSURE_SUCCESS(rv, rv);
popTemplateRule();
mNextInstruction = prevInstr;
rtfHandler = (txRtfHandler*)popResultHandler();
rv = rtfHandler->getAsRTF(&aResult);
NS_ENSURE_SUCCESS(rv, rv);
}
popEvalContext();
// Remove the placeholder and insert the calculated value
mGlobalVariableValues.removeVariable(name);
rv = mGlobalVariableValues.bindVariable(name, aResult);
if (NS_FAILED(rv)) {
NS_RELEASE(aResult);
return rv;
}
return NS_OK;
}
PRBool
txExecutionState::isStripSpaceAllowed(const txXPathNode& aNode)
{
return mStylesheet->isStripSpaceAllowed(aNode, this);
}
void*
txExecutionState::getPrivateContext()
{
return this;
}
txResultRecycler*
txExecutionState::recycler()
{
return mRecycler;
}
void
txExecutionState::receiveError(const nsAString& aMsg, nsresult aRes)
{
// XXX implement me
}
nsresult
txExecutionState::pushEvalContext(txIEvalContext* aContext)
{
nsresult rv = mEvalContextStack.push(mEvalContext);
NS_ENSURE_SUCCESS(rv, rv);
mEvalContext = aContext;
return NS_OK;
}
txIEvalContext*
txExecutionState::popEvalContext()
{
txIEvalContext* prev = mEvalContext;
mEvalContext = (txIEvalContext*)mEvalContextStack.pop();
return prev;
}
nsresult
txExecutionState::pushString(const nsAString& aStr)
{
if (!mStringStack.AppendString(aStr)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
void
txExecutionState::popString(nsAString& aStr)
{
PRInt32 count = mStringStack.Count() - 1;
NS_ASSERTION(count >= 0, "stack is empty");
mStringStack.StringAt(count, aStr);
mStringStack.RemoveStringAt(count);
}
nsresult
txExecutionState::pushInt(PRInt32 aInt)
{
return mIntStack.push(NS_INT32_TO_PTR(aInt));
}
PRInt32
txExecutionState::popInt()
{
return NS_PTR_TO_INT32(mIntStack.pop());
}
nsresult
txExecutionState::pushResultHandler(txAXMLEventHandler* aHandler)
{
nsresult rv = mResultHandlerStack.push(mResultHandler);
NS_ENSURE_SUCCESS(rv, rv);
mResultHandler = aHandler;
return NS_OK;
}
txAXMLEventHandler*
txExecutionState::popResultHandler()
{
txAXMLEventHandler* oldHandler = mResultHandler;
mResultHandler = (txAXMLEventHandler*)mResultHandlerStack.pop();
return oldHandler;
}
nsresult
txExecutionState::pushTemplateRule(txStylesheet::ImportFrame* aFrame,
const txExpandedName& aMode,
txVariableMap* aParams)
{
if (mTemplateRuleCount == mTemplateRulesBufferSize) {
PRInt32 newSize =
mTemplateRulesBufferSize ? mTemplateRulesBufferSize * 2 : 10;
TemplateRule* newRules = new TemplateRule[newSize];
NS_ENSURE_TRUE(newRules, NS_ERROR_OUT_OF_MEMORY);
memcpy(newRules, mTemplateRules,
mTemplateRuleCount * sizeof(TemplateRule));
delete [] mTemplateRules;
mTemplateRules = newRules;
mTemplateRulesBufferSize = newSize;
}
mTemplateRules[mTemplateRuleCount].mFrame = aFrame;
mTemplateRules[mTemplateRuleCount].mModeNsId = aMode.mNamespaceID;
mTemplateRules[mTemplateRuleCount].mModeLocalName = aMode.mLocalName;
mTemplateRules[mTemplateRuleCount].mParams = aParams;
NS_IF_ADDREF(mTemplateRules[mTemplateRuleCount].mModeLocalName);
++mTemplateRuleCount;
return NS_OK;
}
void
txExecutionState::popTemplateRule()
{
NS_PRECONDITION(mTemplateRuleCount > 0, "No rules to pop");
// decrement outside of RELEASE, that would decrement twice
if(mTemplateRuleCount > 0) {
--mTemplateRuleCount;
NS_IF_RELEASE(mTemplateRules[mTemplateRuleCount].mModeLocalName);
}
}
txIEvalContext*
txExecutionState::getEvalContext()
{
return mEvalContext;
}
const txXPathNode*
txExecutionState::retrieveDocument(const nsAString& aUri)
{
NS_ASSERTION(aUri.FindChar(PRUnichar('#')) == kNotFound,
"Remove the fragment.");
if (mDisableLoads) {
return nsnull;
}
PR_LOG(txLog::xslt, PR_LOG_DEBUG,
("Retrieve Document %s", NS_LossyConvertUCS2toASCII(aUri).get()));
// try to get already loaded document
txLoadedDocumentEntry *entry = mLoadedDocuments.PutEntry(aUri);
if (!entry) {
return nsnull;
}
if (!entry->mDocument) {
// open URI
nsAutoString errMsg;
// XXX we should get the loader from the actual node
// triggering the load, but this will do for the time being
nsresult rv;
rv = txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument,
errMsg,
getter_Transfers(entry->mDocument));
if (NS_FAILED(rv) || !entry->mDocument) {
mLoadedDocuments.RawRemoveEntry(entry);
receiveError(NS_LITERAL_STRING("Couldn't load document '") +
aUri + NS_LITERAL_STRING("': ") + errMsg, rv);
return nsnull;
}
}
return entry->mDocument;
}
nsresult
txExecutionState::getKeyNodes(const txExpandedName& aKeyName,
const txXPathNode& aDocument,
const nsAString& aKeyValue,
PRBool aIndexIfNotFound,
txNodeSet** aResult)
{
return mKeyHash.getKeyNodes(aKeyName, aDocument, aKeyValue,
aIndexIfNotFound, *this, aResult);
}
txExecutionState::TemplateRule*
txExecutionState::getCurrentTemplateRule()
{
NS_PRECONDITION(mTemplateRuleCount > 0, "No current rule!");
// better to crash that underrun
return mTemplateRuleCount > 0 ? mTemplateRules + mTemplateRuleCount - 1 : NULL;
}
txInstruction*
txExecutionState::getNextInstruction()
{
txInstruction* instr = mNextInstruction;
if (instr) {
mNextInstruction = instr->mNext;
}
return instr;
}
nsresult
txExecutionState::runTemplate(txInstruction* aTemplate)
{
NS_ENSURE_TRUE(++mRecursionDepth < kMaxRecursionDepth,
NS_ERROR_XSLT_BAD_RECURSION);
nsresult rv = mLocalVarsStack.push(mLocalVariables);
NS_ENSURE_SUCCESS(rv, rv);
rv = mReturnStack.push(mNextInstruction);
NS_ENSURE_SUCCESS(rv, rv);
mLocalVariables = nsnull;
mNextInstruction = aTemplate;
return NS_OK;
}
void
txExecutionState::gotoInstruction(txInstruction* aNext)
{
mNextInstruction = aNext;
}
void
txExecutionState::returnFromTemplate()
{
--mRecursionDepth;
NS_ASSERTION(!mReturnStack.isEmpty() && !mLocalVarsStack.isEmpty(),
"return or variable stack is empty");
delete mLocalVariables;
mNextInstruction = (txInstruction*)mReturnStack.pop();
mLocalVariables = (txVariableMap*)mLocalVarsStack.pop();
}
nsresult
txExecutionState::bindVariable(const txExpandedName& aName,
txAExprResult* aValue)
{
if (!mLocalVariables) {
mLocalVariables = new txVariableMap;
NS_ENSURE_TRUE(mLocalVariables, NS_ERROR_OUT_OF_MEMORY);
}
return mLocalVariables->bindVariable(aName, aValue);
}
void
txExecutionState::removeVariable(const txExpandedName& aName)
{
mLocalVariables->removeVariable(aName);
}
nsresult
txExecutionState::pushParamMap(txVariableMap* aParams)
{
nsresult rv = mParamStack.push(mTemplateParams);
NS_ENSURE_SUCCESS(rv, rv);
mTemplateParams.forget();
mTemplateParams = aParams;
return NS_OK;
}
txVariableMap*
txExecutionState::popParamMap()
{
txVariableMap* oldParams = mTemplateParams.forget();
mTemplateParams = (txVariableMap*)mParamStack.pop();
return oldParams;
}