Search in sources :

Example 16 with ScriptNode

use of org.mozilla.javascript.ast.ScriptNode in project hackpad by dropbox.

the class BodyCodegen method generateResumeGenerator.

// How dispatch to generators works:
// Two methods are generated corresponding to a user-written generator.
// One of these creates a generator object (NativeGenerator), which is
// returned to the user. The other method contains all of the body code
// of the generator.
// When a user calls a generator, the call() method dispatches control to
// to the method that creates the NativeGenerator object. Subsequently when
// the user invokes .next(), .send() or any such method on the generator
// object, the resumeGenerator() below dispatches the call to the
// method corresponding to the generator body. As a matter of convention
// the generator body is given the name of the generator activation function
// appended by "_gen".
private void generateResumeGenerator(ClassFileWriter cfw) {
    boolean hasGenerators = false;
    for (int i = 0; i < scriptOrFnNodes.length; i++) {
        if (isGenerator(scriptOrFnNodes[i]))
            hasGenerators = true;
    }
    // resumeGenerator(). The base class provides a default implementation. 
    if (!hasGenerators)
        return;
    cfw.startMethod("resumeGenerator", "(Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "ILjava/lang/Object;" + "Ljava/lang/Object;)Ljava/lang/Object;", (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
    // load arguments for dispatch to the corresponding *_gen method 
    cfw.addALoad(0);
    cfw.addALoad(1);
    cfw.addALoad(2);
    cfw.addALoad(4);
    cfw.addALoad(5);
    cfw.addILoad(3);
    cfw.addLoadThis();
    cfw.add(ByteCode.GETFIELD, cfw.getClassName(), ID_FIELD_NAME, "I");
    int startSwitch = cfw.addTableSwitch(0, scriptOrFnNodes.length - 1);
    cfw.markTableSwitchDefault(startSwitch);
    int endlabel = cfw.acquireLabel();
    for (int i = 0; i < scriptOrFnNodes.length; i++) {
        ScriptNode n = scriptOrFnNodes[i];
        cfw.markTableSwitchCase(startSwitch, i, (short) 6);
        if (isGenerator(n)) {
            String type = "(" + mainClassSignature + "Lorg/mozilla/javascript/Context;" + "Lorg/mozilla/javascript/Scriptable;" + "Ljava/lang/Object;" + "Ljava/lang/Object;I)Ljava/lang/Object;";
            cfw.addInvoke(ByteCode.INVOKESTATIC, mainClassName, getBodyMethodName(n) + "_gen", type);
            cfw.add(ByteCode.ARETURN);
        } else {
            cfw.add(ByteCode.GOTO, endlabel);
        }
    }
    cfw.markLabel(endlabel);
    pushUndefined(cfw);
    cfw.add(ByteCode.ARETURN);
    // this method uses as many locals as there are arguments (hence 6)
    cfw.stopMethod((short) 6);
}
Also used : ScriptNode(org.mozilla.javascript.ast.ScriptNode)

Example 17 with ScriptNode

use of org.mozilla.javascript.ast.ScriptNode in project hackpad by dropbox.

the class BodyCodegen method generateCode.

private byte[] generateCode(String encodedSource) {
    boolean hasScript = (scriptOrFnNodes[0].getType() == Token.SCRIPT);
    boolean hasFunctions = (scriptOrFnNodes.length > 1 || !hasScript);
    String sourceFile = null;
    if (compilerEnv.isGenerateDebugInfo()) {
        sourceFile = scriptOrFnNodes[0].getSourceName();
    }
    ClassFileWriter cfw = new ClassFileWriter(mainClassName, SUPER_CLASS_NAME, sourceFile);
    cfw.addField(ID_FIELD_NAME, "I", ClassFileWriter.ACC_PRIVATE);
    cfw.addField(DIRECT_CALL_PARENT_FIELD, mainClassSignature, ClassFileWriter.ACC_PRIVATE);
    cfw.addField(REGEXP_ARRAY_FIELD_NAME, REGEXP_ARRAY_FIELD_TYPE, ClassFileWriter.ACC_PRIVATE);
    if (hasFunctions) {
        generateFunctionConstructor(cfw);
    }
    if (hasScript) {
        cfw.addInterface("org/mozilla/javascript/Script");
        generateScriptCtor(cfw);
        generateMain(cfw);
        generateExecute(cfw);
    }
    generateCallMethod(cfw);
    generateResumeGenerator(cfw);
    generateNativeFunctionOverrides(cfw, encodedSource);
    int count = scriptOrFnNodes.length;
    for (int i = 0; i != count; ++i) {
        ScriptNode n = scriptOrFnNodes[i];
        BodyCodegen bodygen = new BodyCodegen();
        bodygen.cfw = cfw;
        bodygen.codegen = this;
        bodygen.compilerEnv = compilerEnv;
        bodygen.scriptOrFn = n;
        bodygen.scriptOrFnIndex = i;
        try {
            bodygen.generateBodyCode();
        } catch (ClassFileWriter.ClassFileFormatException e) {
            throw reportClassFileFormatException(n, e.getMessage());
        }
        if (n.getType() == Token.FUNCTION) {
            OptFunctionNode ofn = OptFunctionNode.get(n);
            generateFunctionInit(cfw, ofn);
            if (ofn.isTargetOfDirectCall()) {
                emitDirectConstructor(cfw, ofn);
            }
        }
    }
    if (directCallTargets != null) {
        int N = directCallTargets.size();
        for (int j = 0; j != N; ++j) {
            cfw.addField(getDirectTargetFieldName(j), mainClassSignature, ClassFileWriter.ACC_PRIVATE);
        }
    }
    emitRegExpInit(cfw);
    emitConstantDudeInitializers(cfw);
    return cfw.toByteArray();
}
Also used : ScriptNode(org.mozilla.javascript.ast.ScriptNode)

Example 18 with ScriptNode

use of org.mozilla.javascript.ast.ScriptNode in project hackpad by dropbox.

the class OptTransformer method detectDirectCall.

private void detectDirectCall(Node node, ScriptNode tree) {
    if (tree.getType() == Token.FUNCTION) {
        Node left = node.getFirstChild();
        // count the arguments
        int argCount = 0;
        Node arg = left.getNext();
        while (arg != null) {
            arg = arg.getNext();
            argCount++;
        }
        if (argCount == 0) {
            OptFunctionNode.get(tree).itsContainsCalls0 = true;
        }
        /*
             * Optimize a call site by converting call("a", b, c) into :
             *
             *  FunctionObjectFor"a" <-- instance variable init'd by constructor
             *
             *  // this is a DIRECTCALL node
             *  fn = GetProp(tmp = GetBase("a"), "a");
             *  if (fn == FunctionObjectFor"a")
             *      fn.call(tmp, b, c)
             *  else
             *      ScriptRuntime.Call(fn, tmp, b, c)
             */
        if (possibleDirectCalls != null) {
            String targetName = null;
            if (left.getType() == Token.NAME) {
                targetName = left.getString();
            } else if (left.getType() == Token.GETPROP) {
                targetName = left.getFirstChild().getNext().getString();
            } else if (left.getType() == Token.GETPROPNOWARN) {
                throw Kit.codeBug();
            }
            if (targetName != null) {
                OptFunctionNode ofn;
                ofn = possibleDirectCalls.get(targetName);
                if (ofn != null && argCount == ofn.fnode.getParamCount() && !ofn.fnode.requiresActivation()) {
                    // for wacky test cases
                    if (argCount <= 32) {
                        node.putProp(Node.DIRECTCALL_PROP, ofn);
                        if (!ofn.isTargetOfDirectCall()) {
                            int index = directCallTargets.size();
                            directCallTargets.add(ofn);
                            ofn.setDirectTargetIndex(index);
                        }
                    }
                }
            }
        }
    }
}
Also used : ScriptNode(org.mozilla.javascript.ast.ScriptNode)

Example 19 with ScriptNode

use of org.mozilla.javascript.ast.ScriptNode in project hackpad by dropbox.

the class Node method toString.

private void toString(ObjToIntMap printIds, StringBuffer sb) {
    if (Token.printTrees) {
        sb.append(Token.name(type));
        if (this instanceof Name) {
            sb.append(' ');
            sb.append(getString());
            Scope scope = getScope();
            if (scope != null) {
                sb.append("[scope: ");
                appendPrintId(scope, printIds, sb);
                sb.append("]");
            }
        } else if (this instanceof Scope) {
            if (this instanceof ScriptNode) {
                ScriptNode sof = (ScriptNode) this;
                if (this instanceof FunctionNode) {
                    FunctionNode fn = (FunctionNode) this;
                    sb.append(' ');
                    sb.append(fn.getName());
                }
                sb.append(" [source name: ");
                sb.append(sof.getSourceName());
                sb.append("] [encoded source length: ");
                sb.append(sof.getEncodedSourceEnd() - sof.getEncodedSourceStart());
                sb.append("] [base line: ");
                sb.append(sof.getBaseLineno());
                sb.append("] [end line: ");
                sb.append(sof.getEndLineno());
                sb.append(']');
            }
            if (((Scope) this).getSymbolTable() != null) {
                sb.append(" [scope ");
                appendPrintId(this, printIds, sb);
                sb.append(": ");
                Iterator<String> iter = ((Scope) this).getSymbolTable().keySet().iterator();
                while (iter.hasNext()) {
                    sb.append(iter.next());
                    sb.append(" ");
                }
                sb.append("]");
            }
        } else if (this instanceof Jump) {
            Jump jump = (Jump) this;
            if (type == Token.BREAK || type == Token.CONTINUE) {
                sb.append(" [label: ");
                appendPrintId(jump.getJumpStatement(), printIds, sb);
                sb.append(']');
            } else if (type == Token.TRY) {
                Node catchNode = jump.target;
                Node finallyTarget = jump.getFinally();
                if (catchNode != null) {
                    sb.append(" [catch: ");
                    appendPrintId(catchNode, printIds, sb);
                    sb.append(']');
                }
                if (finallyTarget != null) {
                    sb.append(" [finally: ");
                    appendPrintId(finallyTarget, printIds, sb);
                    sb.append(']');
                }
            } else if (type == Token.LABEL || type == Token.LOOP || type == Token.SWITCH) {
                sb.append(" [break: ");
                appendPrintId(jump.target, printIds, sb);
                sb.append(']');
                if (type == Token.LOOP) {
                    sb.append(" [continue: ");
                    appendPrintId(jump.getContinue(), printIds, sb);
                    sb.append(']');
                }
            } else {
                sb.append(" [target: ");
                appendPrintId(jump.target, printIds, sb);
                sb.append(']');
            }
        } else if (type == Token.NUMBER) {
            sb.append(' ');
            sb.append(getDouble());
        } else if (type == Token.TARGET) {
            sb.append(' ');
            appendPrintId(this, printIds, sb);
        }
        if (lineno != -1) {
            sb.append(' ');
            sb.append(lineno);
        }
        for (PropListItem x = propListHead; x != null; x = x.next) {
            int type = x.type;
            sb.append(" [");
            sb.append(propToString(type));
            sb.append(": ");
            String value;
            switch(type) {
                case // can't add this as it recurses
                TARGETBLOCK_PROP:
                    value = "target block property";
                    break;
                case // can't add this as it is dull
                LOCAL_BLOCK_PROP:
                    value = "last local block";
                    break;
                case ISNUMBER_PROP:
                    switch(x.intValue) {
                        case BOTH:
                            value = "both";
                            break;
                        case RIGHT:
                            value = "right";
                            break;
                        case LEFT:
                            value = "left";
                            break;
                        default:
                            throw Kit.codeBug();
                    }
                    break;
                case SPECIALCALL_PROP:
                    switch(x.intValue) {
                        case SPECIALCALL_EVAL:
                            value = "eval";
                            break;
                        case SPECIALCALL_WITH:
                            value = "with";
                            break;
                        default:
                            // NON_SPECIALCALL should not be stored
                            throw Kit.codeBug();
                    }
                    break;
                case OBJECT_IDS_PROP:
                    {
                        Object[] a = (Object[]) x.objectValue;
                        value = "[";
                        for (int i = 0; i < a.length; i++) {
                            value += a[i].toString();
                            if (i + 1 < a.length)
                                value += ", ";
                        }
                        value += "]";
                        break;
                    }
                default:
                    Object obj = x.objectValue;
                    if (obj != null) {
                        value = obj.toString();
                    } else {
                        value = String.valueOf(x.intValue);
                    }
                    break;
            }
            sb.append(value);
            sb.append(']');
        }
    }
}
Also used : Scope(org.mozilla.javascript.ast.Scope) ScriptNode(org.mozilla.javascript.ast.ScriptNode) FunctionNode(org.mozilla.javascript.ast.FunctionNode) FunctionNode(org.mozilla.javascript.ast.FunctionNode) Iterator(java.util.Iterator) ScriptNode(org.mozilla.javascript.ast.ScriptNode) Jump(org.mozilla.javascript.ast.Jump) Name(org.mozilla.javascript.ast.Name)

Example 20 with ScriptNode

use of org.mozilla.javascript.ast.ScriptNode in project hackpad by dropbox.

the class NodeTransformer method transformCompilationUnit_r.

private void transformCompilationUnit_r(final ScriptNode tree, final Node parent, Scope scope, boolean createScopeObjects, boolean inStrictMode) {
    Node node = null;
    siblingLoop: for (; ; ) {
        Node previous = null;
        if (node == null) {
            node = parent.getFirstChild();
        } else {
            previous = node;
            node = node.getNext();
        }
        if (node == null) {
            break;
        }
        int type = node.getType();
        if (createScopeObjects && (type == Token.BLOCK || type == Token.LOOP || type == Token.ARRAYCOMP) && (node instanceof Scope)) {
            Scope newScope = (Scope) node;
            if (newScope.getSymbolTable() != null) {
                // transform to let statement so we get a with statement
                // created to contain scoped let variables
                Node let = new Node(type == Token.ARRAYCOMP ? Token.LETEXPR : Token.LET);
                Node innerLet = new Node(Token.LET);
                let.addChildToBack(innerLet);
                for (String name : newScope.getSymbolTable().keySet()) {
                    innerLet.addChildToBack(Node.newString(Token.NAME, name));
                }
                // so we don't transform again
                newScope.setSymbolTable(null);
                Node oldNode = node;
                node = replaceCurrent(parent, previous, node, let);
                type = node.getType();
                let.addChildToBack(oldNode);
            }
        }
        switch(type) {
            case Token.LABEL:
            case Token.SWITCH:
            case Token.LOOP:
                loops.push(node);
                loopEnds.push(((Jump) node).target);
                break;
            case Token.WITH:
                {
                    loops.push(node);
                    Node leave = node.getNext();
                    if (leave.getType() != Token.LEAVEWITH) {
                        Kit.codeBug();
                    }
                    loopEnds.push(leave);
                    break;
                }
            case Token.TRY:
                {
                    Jump jump = (Jump) node;
                    Node finallytarget = jump.getFinally();
                    if (finallytarget != null) {
                        hasFinally = true;
                        loops.push(node);
                        loopEnds.push(finallytarget);
                    }
                    break;
                }
            case Token.TARGET:
            case Token.LEAVEWITH:
                if (!loopEnds.isEmpty() && loopEnds.peek() == node) {
                    loopEnds.pop();
                    loops.pop();
                }
                break;
            case Token.YIELD:
                ((FunctionNode) tree).addResumptionPoint(node);
                break;
            case Token.RETURN:
                {
                    boolean isGenerator = tree.getType() == Token.FUNCTION && ((FunctionNode) tree).isGenerator();
                    if (isGenerator) {
                        node.putIntProp(Node.GENERATOR_END_PROP, 1);
                    }
                    /* 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)
                        // skip the whole mess.
                        break;
                    Node unwindBlock = null;
                    for (int i = loops.size() - 1; i >= 0; i--) {
                        Node n = (Node) loops.get(i);
                        int elemtype = n.getType();
                        if (elemtype == Token.TRY || elemtype == Token.WITH) {
                            Node unwind;
                            if (elemtype == Token.TRY) {
                                Jump jsrnode = new Jump(Token.JSR);
                                Node jsrtarget = ((Jump) n).getFinally();
                                jsrnode.target = jsrtarget;
                                unwind = jsrnode;
                            } else {
                                unwind = new Node(Token.LEAVEWITH);
                            }
                            if (unwindBlock == null) {
                                unwindBlock = new Node(Token.BLOCK, node.getLineno());
                            }
                            unwindBlock.addChildToBack(unwind);
                        }
                    }
                    if (unwindBlock != null) {
                        Node returnNode = node;
                        Node returnExpr = returnNode.getFirstChild();
                        node = replaceCurrent(parent, previous, node, unwindBlock);
                        if (returnExpr == null || isGenerator) {
                            unwindBlock.addChildToBack(returnNode);
                        } else {
                            Node store = new Node(Token.EXPR_RESULT, returnExpr);
                            unwindBlock.addChildToFront(store);
                            returnNode = new Node(Token.RETURN_RESULT);
                            unwindBlock.addChildToBack(returnNode);
                            // transform return expression
                            transformCompilationUnit_r(tree, store, scope, createScopeObjects, inStrictMode);
                        }
                        // skip transformCompilationUnit_r to avoid infinite loop
                        continue siblingLoop;
                    }
                    break;
                }
            case Token.BREAK:
            case Token.CONTINUE:
                {
                    Jump jump = (Jump) node;
                    Jump jumpStatement = jump.getJumpStatement();
                    if (jumpStatement == null)
                        Kit.codeBug();
                    for (int i = loops.size(); ; ) {
                        if (i == 0) {
                            // which should be found
                            throw Kit.codeBug();
                        }
                        --i;
                        Node n = (Node) loops.get(i);
                        if (n == jumpStatement) {
                            break;
                        }
                        int elemtype = n.getType();
                        if (elemtype == Token.WITH) {
                            Node leave = new Node(Token.LEAVEWITH);
                            previous = addBeforeCurrent(parent, previous, node, leave);
                        } else if (elemtype == Token.TRY) {
                            Jump tryNode = (Jump) n;
                            Jump jsrFinally = new Jump(Token.JSR);
                            jsrFinally.target = tryNode.getFinally();
                            previous = addBeforeCurrent(parent, previous, node, jsrFinally);
                        }
                    }
                    if (type == Token.BREAK) {
                        jump.target = jumpStatement.target;
                    } else {
                        jump.target = jumpStatement.getContinue();
                    }
                    jump.setType(Token.GOTO);
                    break;
                }
            case Token.CALL:
                visitCall(node, tree);
                break;
            case Token.NEW:
                visitNew(node, tree);
                break;
            case Token.LETEXPR:
            case Token.LET:
                {
                    Node child = node.getFirstChild();
                    if (child.getType() == Token.LET) {
                        // We have a let statement or expression rather than a
                        // let declaration
                        boolean createWith = tree.getType() != Token.FUNCTION || ((FunctionNode) tree).requiresActivation();
                        node = visitLet(createWith, parent, previous, node);
                        break;
                    } else {
                    // fall through to process let declaration...
                    }
                }
            /* fall through */
            case Token.CONST:
            case Token.VAR:
                {
                    Node result = new Node(Token.BLOCK);
                    for (Node cursor = node.getFirstChild(); cursor != null; ) {
                        // Move cursor to next before createAssignment gets chance
                        // to change n.next
                        Node n = cursor;
                        cursor = cursor.getNext();
                        if (n.getType() == Token.NAME) {
                            if (!n.hasChildren())
                                continue;
                            Node init = n.getFirstChild();
                            n.removeChild(init);
                            n.setType(Token.BINDNAME);
                            n = new Node(type == Token.CONST ? Token.SETCONST : Token.SETNAME, n, init);
                        } else {
                            // to a LETEXPR
                            if (n.getType() != Token.LETEXPR)
                                throw Kit.codeBug();
                        }
                        Node pop = new Node(Token.EXPR_VOID, n, node.getLineno());
                        result.addChildToBack(pop);
                    }
                    node = replaceCurrent(parent, previous, node, result);
                    break;
                }
            case Token.TYPEOFNAME:
                {
                    Scope defining = scope.getDefiningScope(node.getString());
                    if (defining != null) {
                        node.setScope(defining);
                    }
                }
                break;
            case Token.TYPEOF:
            case Token.IFNE:
                {
                    /* We want to suppress warnings for undefined property o.p
                   * for the following constructs: typeof o.p, if (o.p), 
                   * if (!o.p), if (o.p == undefined), if (undefined == o.p)
                   */
                    Node child = node.getFirstChild();
                    if (type == Token.IFNE) {
                        while (child.getType() == Token.NOT) {
                            child = child.getFirstChild();
                        }
                        if (child.getType() == Token.EQ || child.getType() == Token.NE) {
                            Node first = child.getFirstChild();
                            Node last = child.getLastChild();
                            if (first.getType() == Token.NAME && first.getString().equals("undefined"))
                                child = last;
                            else if (last.getType() == Token.NAME && last.getString().equals("undefined"))
                                child = first;
                        }
                    }
                    if (child.getType() == Token.GETPROP)
                        child.setType(Token.GETPROPNOWARN);
                    break;
                }
            case Token.SETNAME:
                if (inStrictMode) {
                    node.setType(Token.STRICT_SETNAME);
                }
            /* fall through */
            case Token.NAME:
            case Token.SETCONST:
            case Token.DELPROP:
                {
                    // Turn name to var for faster access if possible
                    if (createScopeObjects) {
                        break;
                    }
                    Node nameSource;
                    if (type == Token.NAME) {
                        nameSource = node;
                    } else {
                        nameSource = node.getFirstChild();
                        if (nameSource.getType() != Token.BINDNAME) {
                            if (type == Token.DELPROP) {
                                break;
                            }
                            throw Kit.codeBug();
                        }
                    }
                    if (nameSource.getScope() != null) {
                        // already have a scope set
                        break;
                    }
                    String name = nameSource.getString();
                    Scope defining = scope.getDefiningScope(name);
                    if (defining != null) {
                        nameSource.setScope(defining);
                        if (type == Token.NAME) {
                            node.setType(Token.GETVAR);
                        } else if (type == Token.SETNAME || type == Token.STRICT_SETNAME) {
                            node.setType(Token.SETVAR);
                            nameSource.setType(Token.STRING);
                        } else if (type == Token.SETCONST) {
                            node.setType(Token.SETCONSTVAR);
                            nameSource.setType(Token.STRING);
                        } else if (type == Token.DELPROP) {
                            // Local variables are by definition permanent
                            Node n = new Node(Token.FALSE);
                            node = replaceCurrent(parent, previous, node, n);
                        } else {
                            throw Kit.codeBug();
                        }
                    }
                    break;
                }
        }
        transformCompilationUnit_r(tree, node, node instanceof Scope ? (Scope) node : scope, createScopeObjects, inStrictMode);
    }
}
Also used : Scope(org.mozilla.javascript.ast.Scope) ScriptNode(org.mozilla.javascript.ast.ScriptNode) FunctionNode(org.mozilla.javascript.ast.FunctionNode) Jump(org.mozilla.javascript.ast.Jump)

Aggregations

ScriptNode (org.mozilla.javascript.ast.ScriptNode)28 FunctionNode (org.mozilla.javascript.ast.FunctionNode)11 AstRoot (org.mozilla.javascript.ast.AstRoot)4 Jump (org.mozilla.javascript.ast.Jump)4 Scope (org.mozilla.javascript.ast.Scope)4 Date (java.util.Date)2 Iterator (java.util.Iterator)2 Point (org.eclipse.swt.graphics.Point)2 MessageBox (org.eclipse.swt.widgets.MessageBox)2 TableItem (org.eclipse.swt.widgets.TableItem)2 CompilerEnvirons (org.mozilla.javascript.CompilerEnvirons)2 Context (org.mozilla.javascript.Context)2 ErrorReporter (org.mozilla.javascript.ErrorReporter)2 EvaluatorException (org.mozilla.javascript.EvaluatorException)2 JavaScriptException (org.mozilla.javascript.JavaScriptException)2 NodeTransformer (org.mozilla.javascript.NodeTransformer)2 Parser (org.mozilla.javascript.Parser)2 Script (org.mozilla.javascript.Script)2 Scriptable (org.mozilla.javascript.Scriptable)2 ScriptableObject (org.mozilla.javascript.ScriptableObject)2