/*
 * The contents of this file are subject to the Netscape 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/NPL/

 * 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 Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 * Rights Reserved.

 * Contributor(s): 

 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
/* Header file */
#include "js-runtimeexception.h"
#include "js-exceptionhandler.h"
#include "js-nativeglobal.h"
#include "js-variabletable.h"
#include "js-functionnode.h"
#include "js-nodetransformerp.h"

/* Procedures */
/**
 * Return true if the node is a call to a function that requires 
 * access to the enclosing activation object.
 */
jsBool_t JSNodeTransformer::isSpecialCallName(JSNode * tree, JSNode * node)
{
    JSNode *left = node->getFirstChild();
    jsBool_t isSpecial = FALSE;

    if (left->getType() == JSTokenStream::NAME) {
        jsString_t name = left->getString();

        isSpecial = strcmp(name, "eval") == 0 || strcmp(name, "With") == 0;
    }
    else {
        if (left->getType() == JSTokenStream::GETPROP) {
            jsString_t name = left->getLastChild()->getString();

            isSpecial = strcmp(name, "exec") == 0;
        }
    }
    if (isSpecial) {
        // Calls to these functions require activation objects.
        if (inFunction) {
            ((JSFunctionNode *) tree)->setRequiresActivation(TRUE);
        }
        return TRUE;
    }
    return FALSE;
}

/**
 * Return new instance of this class. So that derived classes
 * can override methods of the transformer.
 */
JSNodeTransformer *JSNodeTransformer::newInstance(JSContext * cx)
{
    return new(cx) JSNodeTransformer(cx);
}

JSIRFactory *JSNodeTransformer::createIRFactory(JSContext * cx, JSTokenStream * ts, JSScriptable * scope)
{
    return new(cx) JSIRFactory(ts, scope);
}

JSNode *JSNodeTransformer::transform(JSContext * cx, JSNode * tree, JSNode * enclosing, JSTokenStream * ts, JSScriptable * scope)
{
    inFunction = tree->getType() == JSTokenStream::FUNCTION;
    if (!inFunction) {
        addVariables(cx, tree, getVariableTable(cx, tree));
    }
    irFactory = createIRFactory(cx, ts, scope);

    // to save against upchecks if no finally blocks are used.
    jsBool_t hasFinally = FALSE;

    JSPreorderNodeIterator iterator = tree->getPreorderIterator(cx);
    JSNode *node;

    while ((node = iterator.nextNode()) != NULL) {
        int type = node->getType();

      typeswitch:
        switch (type) {

        case JSTokenStream::FUNCTION:
            if (node == tree) {
                // Add the variables to variable table, the
                // parameters were added earlier.
                JSVariableTable *vars = getVariableTable(cx, tree);
                addVariables(cx, tree, vars);

                // Add return to end if needed.
                JSNode *stmts = node->getLastChild();
                JSNode *lastStmt = stmts->getLastChild();
                if (lastStmt == NULL || lastStmt->getType() != JSTokenStream::RETURN) {
                    stmts->addChildToBack(new(cx) JSNode(JSTokenStream::RETURN));
                }
            }
            else {
                if (inFunction) {
                    // Nested functions require activation  objects.
                    ((JSFunctionNode *) tree)->setRequiresActivation(TRUE);
                }
                JSFunctionNode *fnNode;

                (void)node->getProp(JSNode::FUNCTION_PROP, (JSNode **) & fnNode);
                addParameters(cx, fnNode);

                JSNodeTransformer *inner = newInstance(cx);

                fnNode = (JSFunctionNode *)
                        inner->transform(cx, fnNode, tree, ts, scope);
                node->putProp(cx, JSNode::FUNCTION_PROP, fnNode);

                JSNode::Vector * fns;
                jsBool_t ok = tree->getProp(JSNode::FUNCTION_PROP, &fns);

                if (!ok) {
                    fns = new(cx) JSNode::Vector(cx, 7);
                    tree->putProp(cx, JSNode::FUNCTION_PROP, fns);
                }
                fns->addElement(fnNode);
            }
            break;

        case JSTokenStream::LABEL:{
                JSNode *child = node->getFirstChild();

                node->removeChild(child);

                jsString_t id = child->getString();

                // check against duplicate labels...
                for (int i = loops.size() - 1; i >= 0; i--) {
                    JSNode *n = (JSNode *) loops.elementAt(i);
                    if (n->getType() == JSTokenStream::LABEL) {
                        jsString_t otherId;

                        (void)n->getProp(JSNode::LABEL_PROP, &otherId);
                        if (strcmp(id, otherId) == 0) {
                            jsString_t message = JSContext::getMessage(cx, "msg.dup.label %s",
                                    id);
                            reportMessage(cx, message, node, tree, TRUE, scope);
                            JS_DeleteZoneObj(cx, message);
                            goto typeswitch;
                        }
                    }
                }

                node->putProp(cx, JSNode::LABEL_PROP, id);
                child->release(cx);

                /* Make a target and put it _after_ the following
                 * node.  And in the LABEL node, so breaks get the
                 * right target.
                 */
                JSNode *breakTarget = new(cx) JSNode(JSTokenStream::TARGET);
                JSNode *parent = iterator.getCurrentParent();
                JSNode *next = node->getNextSibling();

                while (next != NULL && (next->getType() == JSTokenStream::LABEL || next->getType() == JSTokenStream::TARGET)) {
                    next = next->getNextSibling();
                }
                if (next == NULL) {
                    break;
                }
                parent->addChildAfter(breakTarget, next);
                node->putProp(cx, JSNode::BREAK_PROP, breakTarget);

                JSNode::Datum *tmp = next->getProp(JSNode::CONTINUE_PROP);
                if (next->getType() == JSTokenStream::LOOP && tmp) {
                    node->putProp(cx, JSNode::CONTINUE_PROP, tmp);
                }

                loops.push(node);
                loopEnds.push(breakTarget);

                break;
            }

        case JSTokenStream::SWITCH:{
                JSNode *breakTarget = new(cx) JSNode(JSTokenStream::TARGET);
                JSNode *parent = iterator.getCurrentParent();
                parent->addChildAfter(breakTarget, node);

                // make all children siblings except for selector
                JSNode *sib = node;
                JSNode *child = node->getFirstChild()->getNext();

                while (child != NULL) {
                    JSNode *next = child->getNext();
                    node->removeChild(child);
                    parent->addChildAfter(child, sib);
                    sib = child;
                    child = next;
                }

                node->putProp(cx, JSNode::BREAK_PROP, breakTarget);
                loops.push(node);
                loopEnds.push(breakTarget);
                node->putProp(cx, JSNode::CASES_PROP, new(cx) JSNode::Vector(cx, 13));
                break;
            }

        case JSTokenStream::DEFAULT:
        case JSTokenStream::CASE:{
                JSNode *sw = (JSNode *) loops.peek();
                if (type == JSTokenStream::CASE) {
                    JSNode::Vector * cases;

                    (void)sw->getProp(JSNode::CASES_PROP, &cases);
                    cases->addElement(node);
                }
                else {
                    sw->putProp(cx, JSNode::DEFAULT_PROP, node);
                }
                break;
            }

        case JSTokenStream::NEWLOCAL:{
                int localCount;
                jsBool_t ok = tree->getProp(JSNode::LOCALCOUNT_PROP, &localCount);

                if (!ok) {
                    tree->putProp(cx, JSNode::LOCALCOUNT_PROP, 1);
                }
                else {
                    tree->putProp(cx, JSNode::LOCALCOUNT_PROP, localCount + 1);
                }
                break;
            }

        case JSTokenStream::LOOP:
            loops.push(node);
            JSNode *breakTarget;

            (void)node->getProp(JSNode::BREAK_PROP, &breakTarget);
            loopEnds.push(breakTarget);
            break;

        case JSTokenStream::WITH:{
                if (inFunction) {
                    // With statements require an activation object.
                    ((JSFunctionNode *) tree)->setRequiresActivation(TRUE);
                }
                loops.push(node);

                JSNode *leave = node->getNextSibling();

                if (leave->getType() != JSTokenStream::LEAVEWITH) {
                    cx->exceptionHandler->throwEx(new(cx) JSRuntimeException("Unexpected tree"));
                }
                loopEnds.push(leave);
                break;
            }

        case JSTokenStream::TRY:{
                JSNode *finallytarget;
                jsBool_t ok = node->getProp(JSNode::FINALLY_PROP, &finallytarget);

                if (ok) {
                    hasFinally = TRUE;
                    loops.push(node);
                    loopEnds.push(finallytarget);
                }
                int localCount;

                ok = tree->getProp(JSNode::LOCALCOUNT_PROP, &localCount);
                if (!ok) {
                    tree->putProp(cx, JSNode::LOCALCOUNT_PROP, 1);
                }
                else {
                    tree->putProp(cx, JSNode::LOCALCOUNT_PROP, localCount + 1);
                }
                break;
            }

        case JSTokenStream::TARGET:
        case JSTokenStream::LEAVEWITH:
            if (!loopEnds.empty() && loopEnds.peek() == node) {
                loopEnds.pop();
                loops.pop();
            }
            break;

        case JSTokenStream::RETURN:{
                /* If we didn't support try/finally, it wouldn't be
                 * necessary to put LEAVEWITH nodes here... but as
                 * we do need a series of JSR FINALLY nodes before
                 * each RETURN, we need to ensure that each finally
                 * block gets the correct scope... which could mean
                 * that some LEAVEWITH nodes are necessary.
                 */
                if (!hasFinally) {
                    break;  // skip the whole mess.
                }
                JSNode *parent = iterator.getCurrentParent();

                for (int i = loops.size() - 1; i >= 0; i--) {
                    JSNode *n = (JSNode *) loops.elementAt(i);
                    int elemtype = n->getType();

                    if (elemtype == JSTokenStream::TRY) {
                        JSNode *jsrnode = new(cx) JSNode(JSTokenStream::JSR);
                        JSNode *jsrtarget;

                        (void)n->getProp(JSNode::FINALLY_PROP, &jsrtarget);
                        jsrnode->putProp(cx, JSNode::TARGET_PROP, jsrtarget);
                        parent->addChildBefore(jsrnode, node);
                    }
                    else if (elemtype == JSTokenStream::WITH) {
                        parent->addChildBefore(new(cx) JSNode(JSTokenStream::LEAVEWITH), node);
                    }
                }
                break;
            }

        case JSTokenStream::BREAK:
        case JSTokenStream::CONTINUE:{
                JSNode *loop = NULL;
                jsBool_t labelled = node->hasChildren();
                jsString_t id = NULL;

                if (labelled) {
                    /* get the label */
                    JSNode *child = node->getFirstChild();
                    id = JS_NewZoneString(cx, child->getString());
                    node->removeChild(child);
                    child->release(cx);
                }
                int i;
                JSNode *parent = iterator.getCurrentParent();

                for (i = loops.size() - 1; i >= 0; i--) {
                    JSNode *n = (JSNode *) loops.elementAt(i);
                    int elemtype = n->getType();

                    if (elemtype == JSTokenStream::WITH) {
                        parent->addChildBefore(new(cx) JSNode(JSTokenStream::LEAVEWITH), node);
                    }
                    else if (elemtype == JSTokenStream::TRY) {
                        JSNode *jsrFinally = new(cx) JSNode(JSTokenStream::JSR);
                        JSNode *jsrTarget;

                        (void)n->getProp(JSNode::FINALLY_PROP, &jsrTarget);

                        jsrFinally->putProp(cx, JSNode::TARGET_PROP, jsrTarget);
                        parent->addChildBefore(jsrFinally, node);
                    }
                    else if (!labelled && (elemtype == JSTokenStream::LOOP || (elemtype == JSTokenStream::SWITCH && type == JSTokenStream::BREAK))) {
                        /* if it's a simple break/continue, break from the
                         * nearest enclosing loop or switch
                         */
                        loop = n;
                        break;
                    }
                    else if (labelled && elemtype == JSTokenStream::LABEL) {
                        jsString_t label;

                        (void)n->getProp(JSNode::LABEL_PROP, &label);
                        if (strcmp(id, label) == 0) {
                            loop = n;
                            break;
                        }
                    }
                }
                int propType = type == JSTokenStream::BREAK ? (int)JSNode::BREAK_PROP : (int)JSNode::CONTINUE_PROP;
                JSNode *target;

                if (loop == NULL) {
                    target = NULL;
                }
                else {
                    jsBool_t ok = loop->getProp(propType, &target);

                    if (!ok)
                        target = NULL;
                }
                if (loop == NULL || target == NULL) {
                    jsString_t message;

                    if (!labelled) {
                        // didn't find an appropriate target
                        if (type == JSTokenStream::CONTINUE) {
                            message = JSContext::getMessage(cx, "msg.continue.outside");
                        }
                        else {
                            message = JSContext::getMessage(cx, "msg.bad.break");
                        }
                    }
                    else if (loop != NULL) {
                        message = JSContext::getMessage(cx, "msg.continue.nonloop");
                    }
                    else {
                        message = JSContext::getMessage(cx, "msg.undef.label %s", id);
                    }
                    reportMessage(cx, message, node, tree, true, scope);
                    JS_DeleteZoneObj(cx, message);
                    node->setType(JSTokenStream::NOP);
                    if (labelled)
                        JS_DeleteZoneObj(cx, id);
                    break;
                }
                node->setType(JSTokenStream::GOTO);
                node->putProp(cx, JSNode::TARGET_PROP, target);
                if (labelled)
                    JS_DeleteZoneObj(cx, id);
                break;
            }

        case JSTokenStream::CALL:
            if (isSpecialCallName(tree, node)) {
                node->putProp(cx, JSNode::SPECIALCALL_PROP, TRUE);
            }
            visitCall(cx, node, tree);
            break;

        case JSTokenStream::NEW:
            if (isSpecialCallName(tree, node)) {
                node->putProp(cx, JSNode::SPECIALCALL_PROP, TRUE);
            }
            visitNew(cx, node, tree);
            break;

        case JSTokenStream::DOT:{
                JSNode *right = node->getLastChild();
                right->setType(JSTokenStream::STRING);
                break;
            }

        case JSTokenStream::EXPRSTMT:
            node->setType(inFunction ? (int)JSTokenStream::POP : (int)JSTokenStream::POPV);
            break;

        case JSTokenStream::OBJECT:{
                JSNode::Vector * regexps;
                jsBool_t ok = tree->getProp(JSNode::REGEXP_PROP, &regexps);

                if (!ok) {
                    regexps = new(cx) JSNode::Vector(cx, 3);
                    tree->putProp(cx, JSNode::REGEXP_PROP, regexps);
                }
                regexps->addElement(node);

                JSNode *n = new(cx) JSNode(JSTokenStream::OBJECT);

                iterator.replaceCurrent(n);
                n->putProp(cx, JSNode::REGEXP_PROP, node);
                break;
            }

        case JSTokenStream::VAR:{
                JSShallowNodeIterator i = node->getChildIterator();
                JSNode *result = new(cx) JSNode(JSTokenStream::BLOCK);

                while (i.hasMoreElements()) {
                    JSNode *n = i.nextNode();

                    if (!n->hasChildren()) {
                        continue;
                    }
                    JSNode *init = n->getFirstChild();

                    n->removeChild(init);
                    JSNode *asn = (JSNode *) irFactory->createAssignment(cx,
                            JSTokenStream::NOP, n, init,
                            JS_T_Null, FALSE);
                    JSNode *pop = new(cx) JSNode(cx, JSTokenStream::POP, asn,
                            node->getDatum());

                    result->addChildToBack(pop);
                }
                iterator.replaceCurrent(result);
                break;
            }

        case JSTokenStream::DELPROP:
        case JSTokenStream::SETNAME:{
                if (!inFunction || inWithStatement()) {
                    break;
                }
                JSNode *bind = node->getFirstChild();

                if (bind == NULL || bind->getType() != JSTokenStream::BINDNAME) {
                    break;
                }
                jsString_t name = bind->getString();

                if (strcmp(name, "arguments") == 0) {
                    // use of "arguments" requires an activation object.
                    ((JSFunctionNode *) tree)->setRequiresActivation(TRUE);
                }
                JSVariableTable *vars = getVariableTable(cx, tree);

                if (vars->get(name) != NULL) {
                    if (type == JSTokenStream::SETNAME) {
                        node->setType(JSTokenStream::SETVAR);
                        bind->setType(JSTokenStream::STRING);
                    }
                    else {
                        // Local variables are by definition permanent
                        JSNode *n = new(cx) JSNode(cx, JSTokenStream::PRIMARY,
                                JSTokenStream::_FALSE);

                        iterator.replaceCurrent(n);
                    }
                }
                break;
            }

        case JSTokenStream::GETPROP:
            if (inFunction) {
                JSNode *n = node->getFirstChild()->getNextSibling();
                jsString_t name = n == NULL ? (jsString_t) "" : n->getString();

                if (strcmp(name, "arguments") == 0 || (strcmp(name, "length") == 0 && cx->getLanguageVersion()
                                == JSContext::VERSION_1_2)) {
                    // Use of "arguments" or "length" in 1.2 requires 
                    // an activation object.
                    ((JSFunctionNode *) tree)->setRequiresActivation(TRUE);
                }
            }
            break;

        case JSTokenStream::NAME:{
                if (!inFunction || inWithStatement()) {
                    break;
                }
                jsString_t name = node->getString();

                if (strcmp(name, "arguments") == 0) {
                    // Use of "arguments" requires an activation object.
                    ((JSFunctionNode *) tree)->setRequiresActivation(TRUE);
                }
                JSVariableTable *vars = getVariableTable(cx, tree);

                if (vars->get(name) != NULL) {
                    node->setType(JSTokenStream::GETVAR);
                }
                break;
            }
        }
    }

    return tree;
}

void JSNodeTransformer::addVariables(JSContext * cx, JSNode * tree, JSVariableTable * vars)
{
    // OPT: a whole pass to collect variables seems expensive.
    // Could special case to go into statements only.
    jsBool_t inFunction = tree->getType() == JSTokenStream::FUNCTION;
    JSPreorderNodeIterator iterator = tree->getPreorderIterator(cx);
    JSVariableName ht(cx);
    JSNode *node;

    while ((node = iterator.nextNode()) != NULL) {
        int nodeType = node->getType();

        if (inFunction && nodeType == JSTokenStream::FUNCTION && node != tree) {
            // In a function with both "var x" and "function x",
            // disregard the var statement, independent of order.
            jsString_t name = node->getString();

            if (name == NULL) {
                continue;
            }
            vars->removeLocal(cx, name);
            ht.put(name, TRUE);
        }
        if (nodeType != JSTokenStream::VAR) {
            continue;
        }
        JSShallowNodeIterator i = node->getChildIterator();
        while (i.hasMoreElements()) {
            JSNode *n = i.nextNode();

            if (ht.get(n->getString()) != TRUE) {
                vars->addLocal(cx, n->getString());
            }
        }
    }
}
void JSNodeTransformer::addParameters(JSContext * cx, JSFunctionNode * fnNode)
{
    JSVariableTable *vars = fnNode->getVariableTable();
    JSNode *args = fnNode->getFirstChild();

    if (args->getType() == JSTokenStream::LP && vars->getParameterCount() == 0) {
        // Add parameters
        JSShallowNodeIterator i = args->getChildIterator();

        while (i.hasMoreElements()) {
            JSNode *n = i.nextNode();
            jsString_t arg = n->getString();

            vars->addParameter(cx, arg);
        }
    }
}
void JSNodeTransformer::visitNew(JSContext * cx, JSNode * node, JSNode * tree)
{
}
void JSNodeTransformer::visitCall(JSContext * cx, JSNode * node, JSNode * tree)
{
    /*
     * For
     *      Call(GetProp(a, b), c, d)   // or GetElem...
     * we wish to evaluate as
     *      Call(GetProp(tmp=a, b), tmp, c, d)
     *
     * for
     *      Call(Name("a"), b, c)
     * we wish to evaluate as
     *      Call(GetProp(tmp=GetBase("a"), "a"), tmp, b, c)
     *
     * and for
     *      Call(a, b, c);
     * we wish to evaluate as
     *      Call(tmp=a, Parent(tmp), c, d)
     */
    JSNode *left = node->getFirstChild();
    // count the arguments
    int argCount = 0;
    JSNode *arg = left->getNextSibling();

    while (arg != NULL) {
        arg = arg->getNextSibling();
        argCount++;
    }
    jsBool_t addGetThis = FALSE;

    if (left->getType() == JSTokenStream::NAME) {
        JSVariableTable *vars = getVariableTable(cx, tree);
        jsString_t name = left->getString();

        if (inFunction && vars->get(name) != NULL && !inWithStatement()) {
            // call to a var. Transform to Call(GetVar("a"), b, c)
            left->setType(JSTokenStream::GETVAR);
            // fall through to code to add GetParent
        }
        else {
            // transform to Call(GetProp(GetBase("a"), "a"), b, c)
            node->removeChild(left);
            left->setType(JSTokenStream::GETBASE);
            JSNode *str = left->cloneNode(cx);

            str->setType(JSTokenStream::STRING);
            JSNode *getProp = new(cx) JSNode(JSTokenStream::GETPROP, left, str);

            node->addChildToFront(getProp);
            left = getProp;

            // Conditionally set a flag to add a GETTHIS node.
            // The getThis entry in the runtime will take a
            // Scriptable object intended to be used as a 'this'
            // and make sure that it is neither a With object or
            // an activation object.
            // Executing getThis requires at least two instanceof
            // tests, so we only include it if we are currently
            // inside a 'with' statement, or if we are executing
            // a script (to protect against an eval inside a with).
            addGetThis = inWithStatement() || !inFunction;
            // fall through to GETPROP code
        }
    }
    if (left->getType() != JSTokenStream::GETPROP && left->getType() != JSTokenStream::GETELEM) {
        node->removeChild(left);

        JSNode *tmp = irFactory->createNewTemp(cx, left);
        JSNode *use = irFactory->createUseTemp(cx, tmp);

        use->putProp(cx, JSNode::TEMP_PROP, tmp);

        JSNode *parent = new(cx) JSNode(JSTokenStream::PARENT, use);

        node->addChildToFront(parent);
        node->addChildToFront(tmp);
        return;
    }
    JSNode *leftLeft = left->getFirstChild();

    left->removeChild(leftLeft);

    JSNode *tmp = irFactory->createNewTemp(cx, leftLeft);

    left->addChildToFront(tmp);

    JSNode *use = irFactory->createUseTemp(cx, tmp);

    use->putProp(cx, JSNode::TEMP_PROP, tmp);
    if (addGetThis) {
        use = new(cx) JSNode(JSTokenStream::GETTHIS, use);
    }
    node->addChildAfter(use, left);
}

jsBool_t JSNodeTransformer::inWithStatement()
{
    for (int i = loops.size() - 1; i >= 0; i--) {
        JSNode *n = (JSNode *) loops.elementAt(i);
        if (n->getType() == JSTokenStream::WITH) {
            return TRUE;
        }
    }
    return FALSE;
}

JSVariableTable *JSNodeTransformer::createVariableTable(JSContext * cx)
{
    return new(cx) JSVariableTable(cx);
}

JSVariableTable *JSNodeTransformer::getVariableTable(JSContext * cx, JSNode * tree)
{
    if (inFunction) {
        return ((JSFunctionNode *) tree)->getVariableTable();
    }
    JSVariableTable *result;
    jsBool_t ok = tree->getProp(JSNode::VARS_PROP, &result);

    if (!ok) {
        result = createVariableTable(cx);
        cx->addVariableTable(result);
        tree->putProp(cx, JSNode::VARS_PROP, result);
    }
    return result;
}

void JSNodeTransformer::reportMessage(JSContext * cx, jsString_t msg, JSNode * stmt, JSNode * tree, jsBool_t isError, JSScriptable * scope)
{
    int lineno;
    jsBool_t ok = stmt->getDatum(&lineno);

    if (!ok)
        lineno = 0;

    jsString_t prop;

    ok = tree->getProp(JSNode::SOURCENAME_PROP, &prop);
    if (!ok)
        prop = NULL;

    if (isError) {
        if (scope != NULL) {
            cx->exceptionHandler->throwEx(JSNativeGlobal::constructError(cx, "SyntaxError", msg, scope, prop, lineno, 0, NULL));
        }
        else {
            JSContext::reportError(cx, msg, prop, lineno, NULL, 0);
        }
    }
    else {
        JSContext::reportWarning(cx, msg, prop, lineno, NULL, 0);
    }
}
void JSNodeTransformer::destroy(JSContext * cx)
{
    irFactory->destroy(cx);
    JSZoneNew::destroy(cx);
}

JSNodeTransformer::JSNodeTransformer(JSContext * cx)
:      loops(&cx->allocator), loopEnds(&cx->allocator)
{
}
