private void generateExpression(Node node, Node parent)
    {
        int type = node.getType();
        Node child = node.getFirstChild();
        switch (type) {
              case Token.USE_STACK:
                break;

              case Token.FUNCTION:
                if (fnCurrent != null || parent.getType() != Token.SCRIPT) {
                    int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
                    OptFunctionNode ofn = OptFunctionNode.get(scriptOrFn,
                                                             fnIndex);
                    int t = ofn.fnode.getFunctionType();
                    if (t != FunctionNode.FUNCTION_EXPRESSION) {
                        throw Codegen.badTree();
                    }
                    visitFunction(ofn, t);
                }
                break;

              case Token.NAME:
                {
                    cfw.addALoad(contextLocal);
                    cfw.addALoad(variableObjectLocal);
                    cfw.addPush(node.getString());
                    addScriptRuntimeInvoke(
                        "name",
                        "(Lorg/mozilla/javascript/Context;"
                        +"Lorg/mozilla/javascript/Scriptable;"
                        +"Ljava/lang/String;"
                        +")Ljava/lang/Object;");
                }
                break;

              case Token.CALL:
              case Token.NEW:
                {
                    int specialType = node.getIntProp(Node.SPECIALCALL_PROP,
                                                      Node.NON_SPECIALCALL);
                    if (specialType == Node.NON_SPECIALCALL) {
                        OptFunctionNode target;
                        target = (OptFunctionNode)node.getProp(
                                     Node.DIRECTCALL_PROP);

                        if (target != null) {
                            visitOptimizedCall(node, target, type, child);
                        } else if (type == Token.CALL) {
                            visitStandardCall(node, child);
                        } else {
                            visitStandardNew(node, child);
                        }
                    } else {
                        visitSpecialCall(node, type, specialType, child);
                    }
                }
                break;

              case Token.REF_CALL:
                generateFunctionAndThisObj(child, node);
                // stack: ... functionObj thisObj
                child = child.getNext();
                generateCallArgArray(node, child, false);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke(
                    "callRef",
                    "(Lorg/mozilla/javascript/Callable;"
                    +"Lorg/mozilla/javascript/Scriptable;"
                    +"[Ljava/lang/Object;"
                    +"Lorg/mozilla/javascript/Context;"
                    +")Lorg/mozilla/javascript/Ref;");
                break;

              case Token.NUMBER:
                {
                    double num = node.getDouble();
                    if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
                        cfw.addPush(num);
                    } else {
                        codegen.pushNumberAsObject(cfw, num);
                    }
                }
                break;

              case Token.STRING:
                cfw.addPush(node.getString());
                break;

              case Token.THIS:
                cfw.addALoad(thisObjLocal);
                break;

              case Token.THISFN:
                cfw.add(ByteCode.ALOAD_0);
                break;

              case Token.NULL:
                cfw.add(ByteCode.ACONST_NULL);
                break;

              case Token.TRUE:
                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
                        "TRUE", "Ljava/lang/Boolean;");
                break;

              case Token.FALSE:
                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
                        "FALSE", "Ljava/lang/Boolean;");
                break;

              case Token.REGEXP:
                {
                    int i = node.getExistingIntProp(Node.REGEXP_PROP);
                    // Scripts can not use REGEXP_ARRAY_FIELD_NAME since
                    // it it will make script.exec non-reentrant so they
                    // store regexp array in a local variable while
                    // functions always access precomputed
                    // REGEXP_ARRAY_FIELD_NAME not to consume locals
                    if (fnCurrent == null) {
                        cfw.addALoad(scriptRegexpLocal);
                    } else {
                        cfw.addALoad(funObjLocal);
                        cfw.add(ByteCode.GETFIELD, codegen.mainClassName,
                                Codegen.REGEXP_ARRAY_FIELD_NAME,
                                Codegen.REGEXP_ARRAY_FIELD_TYPE);
                    }
                    cfw.addPush(i);
                    cfw.add(ByteCode.AALOAD);
                }
                break;

              case Token.COMMA: {
                Node next = child.getNext();
                while (next != null) {
                    generateExpression(child, node);
                    cfw.add(ByteCode.POP);
                    child = next;
                    next = next.getNext();
                }
                generateExpression(child, node);
                break;
              }

              case Token.ENUM_NEXT:
              case Token.ENUM_ID: {
                int local = getLocalBlockRegister(node);
                cfw.addALoad(local);
                if (type == Token.ENUM_NEXT) {
                    addScriptRuntimeInvoke(
                        "enumNext", "(Ljava/lang/Object;)Ljava/lang/Boolean;");
                } else {
                    cfw.addALoad(contextLocal);
                    addScriptRuntimeInvoke("enumId",
                                           "(Ljava/lang/Object;"
                                           +"Lorg/mozilla/javascript/Context;"
                                           +")Ljava/lang/Object;");
                }
                break;
              }

              case Token.ARRAYLIT:
                visitArrayLiteral(node, child);
                break;

              case Token.OBJECTLIT:
                visitObjectLiteral(node, child);
                break;

              case Token.NOT: {
                int trueTarget = cfw.acquireLabel();
                int falseTarget = cfw.acquireLabel();
                int beyond = cfw.acquireLabel();
                generateIfJump(child, node, trueTarget, falseTarget);

                cfw.markLabel(trueTarget);
                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
                                        "FALSE", "Ljava/lang/Boolean;");
                cfw.add(ByteCode.GOTO, beyond);
                cfw.markLabel(falseTarget);
                cfw.add(ByteCode.GETSTATIC, "java/lang/Boolean",
                                        "TRUE", "Ljava/lang/Boolean;");
                cfw.markLabel(beyond);
                cfw.adjustStackTop(-1);
                break;
              }

              case Token.BITNOT:
                generateExpression(child, node);
                addScriptRuntimeInvoke("toInt32", "(Ljava/lang/Object;)I");
                cfw.addPush(-1);         // implement ~a as (a ^ -1)
                cfw.add(ByteCode.IXOR);
                cfw.add(ByteCode.I2D);
                addDoubleWrap();
                break;

              case Token.VOID:
                generateExpression(child, node);
                cfw.add(ByteCode.POP);
                Codegen.pushUndefined(cfw);
                break;

              case Token.TYPEOF:
                generateExpression(child, node);
                addScriptRuntimeInvoke("typeof",
                                       "(Ljava/lang/Object;"
                                       +")Ljava/lang/String;");
                break;

              case Token.TYPEOFNAME:
                visitTypeofname(node);
                break;

              case Token.INC:
              case Token.DEC:
                visitIncDec(node);
                break;

              case Token.OR:
              case Token.AND: {
                    generateExpression(child, node);
                    cfw.add(ByteCode.DUP);
                    addScriptRuntimeInvoke("toBoolean",
                                           "(Ljava/lang/Object;)Z");
                    int falseTarget = cfw.acquireLabel();
                    if (type == Token.AND)
                        cfw.add(ByteCode.IFEQ, falseTarget);
                    else
                        cfw.add(ByteCode.IFNE, falseTarget);
                    cfw.add(ByteCode.POP);
                    generateExpression(child.getNext(), node);
                    cfw.markLabel(falseTarget);
                }
                break;

              case Token.HOOK : {
                    Node ifThen = child.getNext();
                    Node ifElse = ifThen.getNext();
                    generateExpression(child, node);
                    addScriptRuntimeInvoke("toBoolean",
                                           "(Ljava/lang/Object;)Z");
                    int elseTarget = cfw.acquireLabel();
                    cfw.add(ByteCode.IFEQ, elseTarget);
                    short stack = cfw.getStackTop();
                    generateExpression(ifThen, node);
                    int afterHook = cfw.acquireLabel();
                    cfw.add(ByteCode.GOTO, afterHook);
                    cfw.markLabel(elseTarget, stack);
                    generateExpression(ifElse, node);
                    cfw.markLabel(afterHook);
                }
                break;

              case Token.ADD: {
                    generateExpression(child, node);
                    generateExpression(child.getNext(), node);
                    switch (node.getIntProp(Node.ISNUMBER_PROP, -1)) {
                      case Node.BOTH:
                        cfw.add(ByteCode.DADD);
                        break;
                      case Node.LEFT:
                        addOptRuntimeInvoke("add",
                            "(DLjava/lang/Object;)Ljava/lang/Object;");
                        break;
                      case Node.RIGHT:
                        addOptRuntimeInvoke("add",
                            "(Ljava/lang/Object;D)Ljava/lang/Object;");
                        break;
                      default:
                        if (child.getType() == Token.STRING) {
                            addScriptRuntimeInvoke("add",
                                "(Ljava/lang/String;"
                                +"Ljava/lang/Object;"
                                +")Ljava/lang/String;");
                        } else if (child.getNext().getType() == Token.STRING) {
                            addScriptRuntimeInvoke("add",
                                "(Ljava/lang/Object;"
                                +"Ljava/lang/String;"
                                +")Ljava/lang/String;");
                        } else {
                            cfw.addALoad(contextLocal);
                            addScriptRuntimeInvoke("add",
                                "(Ljava/lang/Object;"
                                +"Ljava/lang/Object;"
                                +"Lorg/mozilla/javascript/Context;"
                                +")Ljava/lang/Object;");
                        }
                    }
                }
                break;

              case Token.MUL:
                visitArithmetic(node, ByteCode.DMUL, child, parent);
                break;

              case Token.SUB:
                visitArithmetic(node, ByteCode.DSUB, child, parent);
                break;

              case Token.DIV:
              case Token.MOD:
                visitArithmetic(node, type == Token.DIV
                                      ? ByteCode.DDIV
                                      : ByteCode.DREM, child, parent);
                break;

              case Token.BITOR:
              case Token.BITXOR:
              case Token.BITAND:
              case Token.LSH:
              case Token.RSH:
              case Token.URSH:
                visitBitOp(node, type, child);
                break;

              case Token.POS:
              case Token.NEG:
                generateExpression(child, node);
                addObjectToDouble();
                if (type == Token.NEG) {
                    cfw.add(ByteCode.DNEG);
                }
                addDoubleWrap();
                break;

              case Token.TO_DOUBLE:
                // cnvt to double (not Double)
                generateExpression(child, node);
                addObjectToDouble();
                break;

              case Token.TO_OBJECT: {
                // convert from double
                int prop = -1;
                if (child.getType() == Token.NUMBER) {
                    prop = child.getIntProp(Node.ISNUMBER_PROP, -1);
                }
                if (prop != -1) {
                    child.removeProp(Node.ISNUMBER_PROP);
                    generateExpression(child, node);
                    child.putIntProp(Node.ISNUMBER_PROP, prop);
                } else {
                    generateExpression(child, node);
                    addDoubleWrap();
                }
                break;
              }

              case Token.IN:
              case Token.INSTANCEOF:
              case Token.LE:
              case Token.LT:
              case Token.GE:
              case Token.GT: {
                int trueGOTO = cfw.acquireLabel();
                int falseGOTO = cfw.acquireLabel();
                visitIfJumpRelOp(node, child, trueGOTO, falseGOTO);
                addJumpedBooleanWrap(trueGOTO, falseGOTO);
                break;
              }

              case Token.EQ:
              case Token.NE:
              case Token.SHEQ:
              case Token.SHNE: {
                int trueGOTO = cfw.acquireLabel();
                int falseGOTO = cfw.acquireLabel();
                visitIfJumpEqOp(node, child, trueGOTO, falseGOTO);
                addJumpedBooleanWrap(trueGOTO, falseGOTO);
                break;
              }

              case Token.GETPROP:
              case Token.GETPROPNOWARN:
                visitGetProp(node, child);
                break;

              case Token.GETELEM:
                generateExpression(child, node); // object
                generateExpression(child.getNext(), node);  // id
                cfw.addALoad(contextLocal);
                if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1) {
                    addScriptRuntimeInvoke(
                        "getObjectIndex",
                        "(Ljava/lang/Object;D"
                        +"Lorg/mozilla/javascript/Context;"
                        +")Ljava/lang/Object;");
                }
                else {
                    addScriptRuntimeInvoke(
                        "getObjectElem",
                        "(Ljava/lang/Object;"
                        +"Ljava/lang/Object;"
                        +"Lorg/mozilla/javascript/Context;"
                        +")Ljava/lang/Object;");
                }
                break;

              case Token.GET_REF:
                generateExpression(child, node); // reference
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke(
                    "refGet",
                    "(Lorg/mozilla/javascript/Ref;"
                    +"Lorg/mozilla/javascript/Context;"
                    +")Ljava/lang/Object;");
                break;

              case Token.GETVAR:
                visitGetVar(node);
                break;

              case Token.SETVAR:
                visitSetVar(node, child, true);
                break;

              case Token.SETNAME:
                visitSetName(node, child);
                break;

              case Token.SETCONST:
                visitSetConst(node, child);
                break;

              case Token.SETCONSTVAR:
                visitSetConstVar(node, child, true);
                break;

              case Token.SETPROP:
              case Token.SETPROP_OP:
                visitSetProp(type, node, child);
                break;

              case Token.SETELEM:
              case Token.SETELEM_OP:
                visitSetElem(type, node, child);
                break;

              case Token.SET_REF:
              case Token.SET_REF_OP:
                {
                    generateExpression(child, node);
                    child = child.getNext();
                    if (type == Token.SET_REF_OP) {
                        cfw.add(ByteCode.DUP);
                        cfw.addALoad(contextLocal);
                        addScriptRuntimeInvoke(
                            "refGet",
                            "(Lorg/mozilla/javascript/Ref;"
                            +"Lorg/mozilla/javascript/Context;"
                            +")Ljava/lang/Object;");
                    }
                    generateExpression(child, node);
                    cfw.addALoad(contextLocal);
                    addScriptRuntimeInvoke(
                        "refSet",
                        "(Lorg/mozilla/javascript/Ref;"
                        +"Ljava/lang/Object;"
                        +"Lorg/mozilla/javascript/Context;"
                        +")Ljava/lang/Object;");
                }
                break;

              case Token.DEL_REF:
                generateExpression(child, node);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke("refDel",
                                       "(Lorg/mozilla/javascript/Ref;"
                                       +"Lorg/mozilla/javascript/Context;"
                                       +")Ljava/lang/Object;");
                break;

              case Token.DELPROP:
                generateExpression(child, node);
                child = child.getNext();
                generateExpression(child, node);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke("delete",
                                       "(Ljava/lang/Object;"
                                       +"Ljava/lang/Object;"
                                       +"Lorg/mozilla/javascript/Context;"
                                       +")Ljava/lang/Object;");
                break;

              case Token.BINDNAME:
                {
                    while (child != null) {
                        generateExpression(child, node);
                        child = child.getNext();
                    }
                    // Generate code for "ScriptRuntime.bind(varObj, "s")"
                    cfw.addALoad(contextLocal);
                    cfw.addALoad(variableObjectLocal);
                    cfw.addPush(node.getString());
                    addScriptRuntimeInvoke(
                        "bind",
                        "(Lorg/mozilla/javascript/Context;"
                        +"Lorg/mozilla/javascript/Scriptable;"
                        +"Ljava/lang/String;"
                        +")Lorg/mozilla/javascript/Scriptable;");
                }
                break;

              case Token.LOCAL_LOAD:
                cfw.addALoad(getLocalBlockRegister(node));
                break;

              case Token.REF_SPECIAL:
                {
                    String special = (String)node.getProp(Node.NAME_PROP);
                    generateExpression(child, node);
                    cfw.addPush(special);
                    cfw.addALoad(contextLocal);
                    addScriptRuntimeInvoke(
                        "specialRef",
                        "(Ljava/lang/Object;"
                        +"Ljava/lang/String;"
                        +"Lorg/mozilla/javascript/Context;"
                        +")Lorg/mozilla/javascript/Ref;");
                }
                break;

              case Token.REF_MEMBER:
              case Token.REF_NS_MEMBER:
              case Token.REF_NAME:
              case Token.REF_NS_NAME:
                {
                    int memberTypeFlags
                        = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
                    // generate possible target, possible namespace and member
                    do {
                        generateExpression(child, node);
                        child = child.getNext();
                    } while (child != null);
                    cfw.addALoad(contextLocal);
                    String methodName, signature;
                    switch (type) {
                      case Token.REF_MEMBER:
                        methodName = "memberRef";
                        signature = "(Ljava/lang/Object;"
                                    +"Ljava/lang/Object;"
                                    +"Lorg/mozilla/javascript/Context;"
                                    +"I"
                                    +")Lorg/mozilla/javascript/Ref;";
                        break;
                      case Token.REF_NS_MEMBER:
                        methodName = "memberRef";
                        signature = "(Ljava/lang/Object;"
                                    +"Ljava/lang/Object;"
                                    +"Ljava/lang/Object;"
                                    +"Lorg/mozilla/javascript/Context;"
                                    +"I"
                                    +")Lorg/mozilla/javascript/Ref;";
                        break;
                      case Token.REF_NAME:
                        methodName = "nameRef";
                        signature = "(Ljava/lang/Object;"
                                    +"Lorg/mozilla/javascript/Context;"
                                    +"Lorg/mozilla/javascript/Scriptable;"
                                    +"I"
                                    +")Lorg/mozilla/javascript/Ref;";
                        cfw.addALoad(variableObjectLocal);
                        break;
                      case Token.REF_NS_NAME:
                        methodName = "nameRef";
                        signature = "(Ljava/lang/Object;"
                                    +"Lorg/mozilla/javascript/Context;"
                                    +"Lorg/mozilla/javascript/Scriptable;"
                                    +"I"
                                    +")Lorg/mozilla/javascript/Ref;";
                        cfw.addALoad(variableObjectLocal);
                        break;
                      default:
                        throw Kit.codeBug();
                    }
                    cfw.addPush(memberTypeFlags);
                    addScriptRuntimeInvoke(methodName, signature);
                }
                break;

              case Token.DOTQUERY:
                visitDotQuery(node, child);
                break;

              case Token.ESCXMLATTR:
                generateExpression(child, node);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke("escapeAttributeValue",
                                       "(Ljava/lang/Object;"
                                       +"Lorg/mozilla/javascript/Context;"
                                       +")Ljava/lang/String;");
                break;

              case Token.ESCXMLTEXT:
                generateExpression(child, node);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke("escapeTextValue",
                                       "(Ljava/lang/Object;"
                                       +"Lorg/mozilla/javascript/Context;"
                                       +")Ljava/lang/String;");
                break;

              case Token.DEFAULTNAMESPACE:
                generateExpression(child, node);
                cfw.addALoad(contextLocal);
                addScriptRuntimeInvoke("setDefaultNamespace",
                                       "(Ljava/lang/Object;"
                                       +"Lorg/mozilla/javascript/Context;"
                                       +")Ljava/lang/Object;");
                break;

              case Token.YIELD:
                generateYieldPoint(node, true);
                break;

              case Token.WITHEXPR: {
                Node enterWith = child;
                Node with = enterWith.getNext();
                Node leaveWith = with.getNext();
                generateStatement(enterWith);
                generateExpression(with.getFirstChild(), with);
                generateStatement(leaveWith);
                break;
              }
              
              case Token.ARRAYCOMP: {
                Node initStmt = child;
                Node expr = child.getNext();
                generateStatement(initStmt);
                generateExpression(expr, node);
                break;
              }
              
              default:
                throw new RuntimeException("Unexpected node type "+type);
        }

    }