dijkstra.gen.DijkstraCodeGenerator.java Source code

Java tutorial

Introduction

Here is the source code for dijkstra.gen.DijkstraCodeGenerator.java

Source

/*******************************************************************************
 * Copyright (c) 2015 Gary F. Pollice
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Used in CS4533/CS544 at Worcester Polytechnic Institute
 *******************************************************************************/

package dijkstra.gen;

import static dijkstra.ast.ASTNode.ASTNodeType.FUNCALL;
import static dijkstra.utility.DijkstraType.FUNCTION;
import static dijkstra.utility.DijkstraType.UNDEFINED;
import static org.objectweb.asm.Opcodes.AALOAD;
import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.BALOAD;
import static org.objectweb.asm.Opcodes.BASTORE;
import static org.objectweb.asm.Opcodes.BIPUSH;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.D2I;
import static org.objectweb.asm.Opcodes.D2L;
import static org.objectweb.asm.Opcodes.DADD;
import static org.objectweb.asm.Opcodes.DALOAD;
import static org.objectweb.asm.Opcodes.DASTORE;
import static org.objectweb.asm.Opcodes.DCMPL;
import static org.objectweb.asm.Opcodes.DDIV;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DMUL;
import static org.objectweb.asm.Opcodes.DNEG;
import static org.objectweb.asm.Opcodes.DSTORE;
import static org.objectweb.asm.Opcodes.DSUB;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFGE;
import static org.objectweb.asm.Opcodes.IFGT;
import static org.objectweb.asm.Opcodes.IFLE;
import static org.objectweb.asm.Opcodes.IFLT;
import static org.objectweb.asm.Opcodes.IFNE;
import static org.objectweb.asm.Opcodes.IF_ICMPEQ;
import static org.objectweb.asm.Opcodes.IF_ICMPNE;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.ISTORE;
import static org.objectweb.asm.Opcodes.L2D;
import static org.objectweb.asm.Opcodes.L2I;
import static org.objectweb.asm.Opcodes.LADD;
import static org.objectweb.asm.Opcodes.LALOAD;
import static org.objectweb.asm.Opcodes.LASTORE;
import static org.objectweb.asm.Opcodes.LCMP;
import static org.objectweb.asm.Opcodes.LDIV;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LMUL;
import static org.objectweb.asm.Opcodes.LNEG;
import static org.objectweb.asm.Opcodes.LREM;
import static org.objectweb.asm.Opcodes.LSTORE;
import static org.objectweb.asm.Opcodes.LSUB;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.NEWARRAY;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.SIPUSH;
import static org.objectweb.asm.Opcodes.T_BOOLEAN;
import static org.objectweb.asm.Opcodes.T_DOUBLE;
import static org.objectweb.asm.Opcodes.T_LONG;
import static org.objectweb.asm.Opcodes.V1_8;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import dijkstra.ast.ASTNode;
import dijkstra.ast.ASTNode.ASTNodeType;
import dijkstra.ast.ASTNodeFactory.AlternativeNode;
import dijkstra.ast.ASTNodeFactory.ArrayAccessorNode;
import dijkstra.ast.ASTNodeFactory.ArrayDeclarationNode;
import dijkstra.ast.ASTNodeFactory.AssignListNode;
import dijkstra.ast.ASTNodeFactory.AssignNode;
import dijkstra.ast.ASTNodeFactory.AttributeNode;
import dijkstra.ast.ASTNodeFactory.BinaryExpressionNode;
import dijkstra.ast.ASTNodeFactory.ClassDeclarationNode;
import dijkstra.ast.ASTNodeFactory.ConstantNode;
import dijkstra.ast.ASTNodeFactory.FunctionCallNode;
import dijkstra.ast.ASTNodeFactory.FunctionDeclarationNode;
import dijkstra.ast.ASTNodeFactory.GuardNode;
import dijkstra.ast.ASTNodeFactory.IDNode;
import dijkstra.ast.ASTNodeFactory.InputNode;
import dijkstra.ast.ASTNodeFactory.IterativeNode;
import dijkstra.ast.ASTNodeFactory.MethodCallNode;
import dijkstra.ast.ASTNodeFactory.OutputNode;
import dijkstra.ast.ASTNodeFactory.ProcedureCallNode;
import dijkstra.ast.ASTNodeFactory.ProcedureDeclarationNode;
import dijkstra.ast.ASTNodeFactory.ProgramNode;
import dijkstra.ast.ASTNodeFactory.PropertyNode;
import dijkstra.ast.ASTNodeFactory.ReturnNode;
import dijkstra.ast.ASTNodeFactory.RootNode;
import dijkstra.ast.ASTNodeFactory.UnaryExpressionNode;
import dijkstra.ast.ASTNodeFactory.VariableDeclarationNode;
import dijkstra.ast.ASTVisitor;
import dijkstra.lexparse.DijkstraParser;
import dijkstra.symbol.ArraySymbol;
import dijkstra.symbol.ClassSymbol;
import dijkstra.symbol.FunctionSymbol;
import dijkstra.symbol.ObjectSymbol;
import dijkstra.symbol.ProcedureSymbol;
import dijkstra.symbol.Symbol;
import dijkstra.symbol.SymbolTableManager;
import dijkstra.utility.DijkstraType;

/**
 * Description
 * @version Feb 21, 2015
 */
public class DijkstraCodeGenerator extends ASTVisitor<byte[]> {
    private final boolean debug = true;
    private ClassWriter cw = null;
    private final Stack<MethodVisitor> mvStack;

    private final String DEFAULT_PACKAGE = "djkcode";
    private String classPackage;
    final private Stack<Label> guardLabelStack;
    final private Stack<Symbol> returnArrayStack;
    final private Stack<Symbol> returnIndexStack;
    private String programName = null;
    private String fullPath = null;

    private boolean reading = true;
    private boolean inClass = false;
    private boolean processingClassFields = false;

    public DijkstraCodeGenerator() {
        classPackage = DEFAULT_PACKAGE;
        guardLabelStack = new Stack<Label>();
        mvStack = new Stack<MethodVisitor>();
        returnArrayStack = new Stack<Symbol>();
        returnIndexStack = new Stack<Symbol>();
    }

    public byte[] visit(RootNode root) {
        return root.getProgramNode().accept(this);
    }

    public byte[] visit(ClassDeclarationNode classDecl) {
        // prolog
        cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        programName = classDecl.getID().getName();
        fullPath = classPackage + "/" + classDecl.getID().getName();
        cw.visit(V1_8, ACC_PUBLIC + ACC_STATIC, fullPath, null, "java/lang/Object", null);
        cw.visitSource(classDecl.getID().getName() + ".java", null);

        FieldVisitor fv;
        List<Symbol> classSymbols = new ArrayList<Symbol>();
        classSymbols.addAll(SymbolTableManager.getInstance().getClassSymbolManager(programName)
                .getCurrentSymbolTable().getSymbols());
        for (ASTNode property : classDecl.getProperties()) {
            classSymbols.add(((PropertyNode) property).getID().symbol);
        }
        for (Symbol symbol : classSymbols) {
            String type;
            if (symbol.getType() == DijkstraType.ARRAY) {
                ArraySymbol as = (ArraySymbol) symbol;
                if (as.getArrayType() == DijkstraType.INT) {
                    type = "[J";
                } else if (as.getArrayType() == DijkstraType.FLOAT) {
                    type = "[D";
                } else {
                    type = "[Z";
                }
            } else {
                if (symbol.getType() == DijkstraType.INT) {
                    type = "J";
                } else if (symbol.getType() == DijkstraType.FLOAT) {
                    type = "D";
                } else {
                    type = "Z";
                }
            }
            fv = cw.visitField(ACC_PUBLIC, symbol.getId(), type, null, null);
            fv.visitEnd();
        }
        final StringBuilder sig = new StringBuilder();
        sig.append('(');
        for (Symbol property : ((ClassSymbol) classDecl.getID().symbol).getPropertySymbols()) {
            if (property.getType() == DijkstraType.INT) {
                sig.append('J');
            } else if (property.getType() == DijkstraType.FLOAT) {
                sig.append('D');
            } else {
                sig.append('Z');
            }
        }
        sig.append(")V");
        mvStack.push(cw.visitMethod(ACC_PUBLIC, "<init>", sig.toString(), null, null));
        MethodVisitor mv = mvStack.peek();
        mv.visitCode();
        final Label startLabel = new Label();
        mv.visitLabel(startLabel);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);

        int localAddr = 1;
        for (Symbol param : ((ClassSymbol) classDecl.getID().symbol).getPropertySymbols()) {
            mv.visitVarInsn(ALOAD, 0);
            if (param.getType() == DijkstraType.INT) {
                mv.visitVarInsn(LLOAD, localAddr);
                mv.visitFieldInsn(PUTFIELD, fullPath, param.getId(), "J");
                localAddr += 2;
            } else if (param.getType() == DijkstraType.FLOAT) {
                mv.visitVarInsn(DLOAD, localAddr);
                mv.visitFieldInsn(PUTFIELD, fullPath, param.getId(), "D");
                localAddr += 2;
            } else {
                mv.visitVarInsn(ILOAD, localAddr);
                mv.visitFieldInsn(PUTFIELD, fullPath, param.getId(), "Z");
                localAddr += 1;
            }
        }

        processingClassFields = true;
        inClass = true;
        for (ASTNode body : classDecl.getDeclarations()) {
            body.accept(this);
        }
        inClass = false;
        processingClassFields = false;

        mv.visitInsn(RETURN);
        final Label endLabel = new Label();
        mv.visitLabel(endLabel);
        mv.visitLocalVariable("this", "L" + fullPath + ";", null, startLabel, endLabel, 0);
        int paramLoc = 1;
        for (Symbol param : ((ClassSymbol) classDecl.getID().symbol).getPropertySymbols()) {
            if (param.getType() == DijkstraType.INT) {
                mv.visitLocalVariable(param.getId(), "J", null, startLabel, endLabel, paramLoc);
                paramLoc += 2;
            } else if (param.getType() == DijkstraType.FLOAT) {
                mv.visitLocalVariable(param.getId(), "D", null, startLabel, endLabel, paramLoc);
                paramLoc += 2;
            } else {
                mv.visitLocalVariable(param.getId(), "Z", null, startLabel, endLabel, paramLoc);
                paramLoc += 1;
            }
        }
        mv.visitMaxs(0, 0);
        mv.visitEnd();
        mvStack.pop();

        inClass = true;
        for (ASTNode body : classDecl.getDeclarations()) {
            body.accept(this);
        }
        inClass = false;

        // Actual end of generation
        cw.visitEnd();
        return cw.toByteArray();
    }

    /**
     * Generate the program prolog, then visit the children, then generate
     * the program end.
     * @see dijkstra.ast.ASTVisitor#visit(dijkstra.ast.ASTNodeFactory.ProgramNode)
     */
    public byte[] visit(ProgramNode program) {
        // prolog
        cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        programName = program.programName;
        fullPath = classPackage + "/" + program.programName;
        cw.visit(V1_8, ACC_PUBLIC + ACC_STATIC, fullPath, null, "java/lang/Object", null);
        cw.visitSource(program.programName + ".java", null);

        FieldVisitor fv;
        for (Symbol symbol : SymbolTableManager.getInstance().getCurrentSymbolTable().getSymbols()) {
            String type;
            if (symbol.getType() == DijkstraType.ARRAY) {
                ArraySymbol as = (ArraySymbol) symbol;
                if (as.getArrayType() == DijkstraType.INT) {
                    type = "[J";
                } else if (as.getArrayType() == DijkstraType.FLOAT) {
                    type = "[D";
                } else if (as.getArrayType() == DijkstraType.BOOLEAN) {
                    type = "[Z";
                } else {
                    type = "L" + classPackage + "/" + ((ObjectSymbol) symbol).getObjectType() + ";";
                }
            } else {
                if (symbol.getType() == DijkstraType.INT) {
                    type = "J";
                } else if (symbol.getType() == DijkstraType.FLOAT) {
                    type = "D";
                } else if (symbol.getType() == DijkstraType.BOOLEAN) {
                    type = "Z";
                } else {
                    type = "L" + classPackage + "/" + ((ObjectSymbol) symbol).getObjectType() + ";";
                }
            }
            fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, symbol.getId(), type, null, null);
            fv.visitEnd();
        }
        mvStack.push(cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null));
        MethodVisitor mv = mvStack.peek();
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
        mvStack.pop();
        // Start the main() method
        mvStack.push(cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null));
        mv = mvStack.peek();
        mv.visitCode();

        visitChildren(program);

        // program end
        //  End of main
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();

        // Actual end of generation
        cw.visitEnd();
        return cw.toByteArray();
    }

    public byte[] visit(AttributeNode node) {
        final MethodVisitor mv = mvStack.peek();
        final String objectType = ((ObjectSymbol) node.getObj().symbol).getObjectType();
        if (reading) {
            if (node.getObj().symbol.isGlobal() && !inClass) {
                mv.visitFieldInsn(GETSTATIC, fullPath, node.getObj().getName(),
                        "L" + classPackage + "/" + ((ObjectSymbol) node.getObj().symbol).getObjectType() + ";");
            } else if (node.getObj().symbol.isGlobal() && inClass) {
                mv.visitFieldInsn(GETFIELD, fullPath, node.getObj().getName(),
                        "L" + classPackage + "/" + ((ObjectSymbol) node.getObj().symbol).getObjectType() + ";");
            } else {
                mv.visitVarInsn(ALOAD, node.getObj().getAddress());
            }
            if (node.getType() == DijkstraType.INT) {
                mv.visitFieldInsn(GETFIELD, classPackage + "/" + objectType, node.getAttribute().getName(), "J");
            } else if (node.getType() == DijkstraType.FLOAT) {
                mv.visitFieldInsn(GETFIELD, classPackage + "/" + objectType, node.getAttribute().getName(), "D");
            } else if (node.getType() == DijkstraType.BOOLEAN) {
                mv.visitFieldInsn(GETFIELD, classPackage + "/" + objectType, node.getAttribute().getName(), "Z");
            } else {
                mv.visitFieldInsn(GETFIELD, classPackage + "/" + objectType, node.getAttribute().getName(),
                        "L" + classPackage + "/" + ((ObjectSymbol) node.getObj().symbol).getObjectType() + ";");
            }
        } else {
            if (node.getType() == DijkstraType.INT) {
                mv.visitFieldInsn(PUTFIELD, classPackage + "/" + objectType, node.getAttribute().getName(), "J");
            } else if (node.getType() == DijkstraType.FLOAT) {
                mv.visitFieldInsn(PUTFIELD, classPackage + "/" + objectType, node.getAttribute().getName(), "D");
            } else if (node.getType() == DijkstraType.BOOLEAN) {
                mv.visitFieldInsn(PUTFIELD, classPackage + "/" + objectType, node.getAttribute().getName(), "Z");
            } else {
                mv.visitFieldInsn(PUTFIELD, classPackage + "/" + objectType, node.getAttribute().getName(),
                        "L" + classPackage + "/" + ((ObjectSymbol) node.getObj().symbol).getObjectType() + ";");
            }
        }
        return null;
    }

    public byte[] visit(VariableDeclarationNode decl) {
        if (!mvStack.isEmpty() && !processingClassFields) {
            final MethodVisitor mv = mvStack.peek();
            for (IDNode id : decl.getIdList()) {
                if (id.getType() == DijkstraType.INT) {
                    mv.visitLdcInsn(new Long(0));
                    if (id.symbol.isGlobal()) {
                        mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "J");
                    } else {
                        mv.visitVarInsn(LSTORE, id.getAddress());
                    }
                } else if (id.getType() == DijkstraType.FLOAT) {
                    mv.visitLdcInsn(new Double(0));
                    if (id.symbol.isGlobal()) {
                        mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "D");
                    } else {
                        mv.visitVarInsn(DSTORE, id.getAddress());
                    }
                } else if (id.getType() == DijkstraType.BOOLEAN) {
                    // boolean
                    mv.visitInsn(ICONST_0);
                    if (id.symbol.isGlobal()) {
                        mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "Z");
                    } else {
                        mv.visitVarInsn(ISTORE, id.getAddress());
                    }
                }
            }
        } else if (processingClassFields) {
            final MethodVisitor mv = mvStack.peek();

        }
        return null;
    }

    public byte[] visit(ArrayDeclarationNode decl) {
        if (!mvStack.isEmpty() && !processingClassFields) {
            final MethodVisitor mv = mvStack.peek();
            for (IDNode id : decl.getIdList()) {
                decl.arrayLength.accept(this);
                if (decl.arrayLength.type == DijkstraType.INT) {
                    mv.visitInsn(L2I);
                } else {
                    mv.visitInsn(D2I);
                }
                ArraySymbol symbol = ((ArraySymbol) id.symbol);
                if (id.symbol.isGlobal()) {
                    if (symbol.getArrayType() == DijkstraType.INT) {
                        mv.visitIntInsn(NEWARRAY, T_LONG);
                        mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "[J");
                    } else if (symbol.getArrayType() == DijkstraType.FLOAT) {
                        mv.visitIntInsn(NEWARRAY, T_DOUBLE);
                        mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "[D");
                    } else if (symbol.getArrayType() == DijkstraType.BOOLEAN) {
                        mv.visitIntInsn(NEWARRAY, T_BOOLEAN);
                        mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "[Z");
                    }
                } else {
                    if (symbol.getArrayType() == DijkstraType.INT) {
                        mv.visitIntInsn(NEWARRAY, T_LONG);
                        mv.visitVarInsn(ASTORE, id.getAddress());
                    } else if (symbol.getArrayType() == DijkstraType.FLOAT) {
                        mv.visitIntInsn(NEWARRAY, T_DOUBLE);
                        mv.visitVarInsn(ASTORE, id.getAddress());
                    } else if (symbol.getArrayType() == DijkstraType.BOOLEAN) {
                        // boolean
                        mv.visitIntInsn(NEWARRAY, T_BOOLEAN);
                        mv.visitVarInsn(ASTORE, id.getAddress());
                    }
                }
            }
        } else if (processingClassFields) {
            final MethodVisitor mv = mvStack.peek();
            for (IDNode id : decl.getIdList()) {
                mv.visitVarInsn(ALOAD, 0);
                decl.arrayLength.accept(this);
                if (decl.arrayLength.type == DijkstraType.INT) {
                    mv.visitInsn(L2I);
                } else {
                    mv.visitInsn(D2I);
                }
                ArraySymbol symbol = ((ArraySymbol) id.symbol);
                if (symbol.getArrayType() == DijkstraType.INT) {
                    mv.visitIntInsn(NEWARRAY, T_LONG);
                    mv.visitFieldInsn(PUTFIELD, fullPath, id.getName(), "[J");
                } else if (symbol.getArrayType() == DijkstraType.FLOAT) {
                    mv.visitIntInsn(NEWARRAY, T_DOUBLE);
                    mv.visitFieldInsn(PUTFIELD, fullPath, id.getName(), "[D");
                } else if (symbol.getArrayType() == DijkstraType.BOOLEAN) {
                    mv.visitIntInsn(NEWARRAY, T_BOOLEAN);
                    mv.visitFieldInsn(PUTFIELD, fullPath, id.getName(), "[Z");
                }
            }
        }
        return null;
    }

    /**
     * Call the runtime input for the ID and then store the result in the 
     * @see dijkstra.ast.ASTVisitor#visit(dijkstra.ast.ASTNodeFactory.InputNode)
     */
    public byte[] visit(InputNode input) {
        final MethodVisitor mv = mvStack.peek();
        for (IDNode id : input.getIDs()) {
            mv.visitLdcInsn(id.token.getText()); // Name of the variable
            if (id.getType() == DijkstraType.INT) {
                mv.visitMethodInsn(INVOKESTATIC, "dijkstra/runtime/DijkstraRuntime", "inputLong",
                        "(Ljava/lang/String;)J", false);
                if (id.symbol.isGlobal()) {
                    mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "J");
                } else {
                    mv.visitVarInsn(LSTORE, id.getAddress());
                }
            } else if (id.getType() == DijkstraType.FLOAT) {
                mv.visitMethodInsn(INVOKESTATIC, "dijkstra/runtime/DijkstraRuntime", "inputDouble",
                        "(Ljava/lang/String;)D", false);
                if (id.symbol.isGlobal()) {
                    mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "D");
                } else {
                    mv.visitVarInsn(DSTORE, id.getAddress());
                }
            } else {
                mv.visitMethodInsn(INVOKESTATIC, "dijkstra/runtime/DijkstraRuntime", "inputBoolean",
                        "(Ljava/lang/String;)Z", false);
                if (id.symbol.isGlobal()) {
                    mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "Z");
                } else {
                    mv.visitVarInsn(ISTORE, id.getAddress());
                }
            }
        }
        return null;
    }

    /**
     * Get the expression onto the stack and then call the runtime print
     * routine.
     * @see dijkstra.ast.ASTVisitor#visit(dijkstra.ast.ASTNodeFactory.OutputNode)
     */
    public byte[] visit(OutputNode output) {
        final MethodVisitor mv = mvStack.peek();
        output.getExpression().accept(this); // TOS = expression value
        if (output.getType() == DijkstraType.INT) {
            mv.visitMethodInsn(INVOKESTATIC, "dijkstra/runtime/DijkstraRuntime", "printLong", "(J)V", false);
        } else if (output.getType() == DijkstraType.FLOAT) {
            mv.visitMethodInsn(INVOKESTATIC, "dijkstra/runtime/DijkstraRuntime", "printDouble", "(D)V", false);
        } else {
            // Boolean
            mv.visitMethodInsn(INVOKESTATIC, "dijkstra/runtime/DijkstraRuntime", "printBoolean", "(Z)V", false);
        }
        return null;
    }

    public byte[] visit(ArrayAccessorNode arrayAccessor) {
        final MethodVisitor mv = mvStack.peek();
        final DijkstraType arrayType = ((ArraySymbol) arrayAccessor.getId().symbol).getArrayType();
        if (reading) {
            if (arrayAccessor.getId().symbol.isGlobal() && !inClass) {
                if (arrayType == DijkstraType.INT) {
                    mv.visitFieldInsn(GETSTATIC, fullPath, arrayAccessor.getId().getName(), "[J");
                } else if (arrayType == DijkstraType.FLOAT) {
                    mv.visitFieldInsn(GETSTATIC, fullPath, arrayAccessor.getId().getName(), "[D");
                } else {
                    mv.visitFieldInsn(GETSTATIC, fullPath, arrayAccessor.getId().getName(), "[Z");
                }
            } else if (arrayAccessor.getId().symbol.isGlobal() && inClass) {
                if (arrayType == DijkstraType.INT) {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, arrayAccessor.getId().getName(), "[J");
                } else if (arrayType == DijkstraType.FLOAT) {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, arrayAccessor.getId().getName(), "[D");
                } else {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, arrayAccessor.getId().getName(), "[Z");
                }
            } else {
                mv.visitVarInsn(ALOAD, arrayAccessor.getId().getAddress());
            }
            arrayAccessor.getExpression().accept(this);
            if (arrayAccessor.getExpression().getType() == DijkstraType.FLOAT) {
                mv.visitInsn(D2I);
            } else {
                mv.visitInsn(L2I);
            }
            if (arrayType == DijkstraType.INT) {
                mv.visitInsn(LALOAD);
            } else if (arrayType == DijkstraType.FLOAT) {
                mv.visitInsn(DALOAD);
            } else {
                mv.visitInsn(BALOAD);
            }
        } else {
            if (arrayType == DijkstraType.INT) {
                mv.visitInsn(LASTORE);
            } else if (arrayType == DijkstraType.FLOAT) {
                mv.visitInsn(DASTORE);
            } else {
                mv.visitInsn(BASTORE);
            }
        }
        return null;
    }

    public byte[] visit(AssignNode assign) {
        final MethodVisitor mv = mvStack.peek();
        if (assign.getVar() instanceof ArrayAccessorNode) {
            final ArrayAccessorNode arrAcc = ((ArrayAccessorNode) assign.getVar());
            // Load the pointer to the array
            final DijkstraType arrayType = ((ArraySymbol) arrAcc.getId().symbol).getArrayType();
            if (arrAcc.getId().symbol.isGlobal()) {
                if (arrayType == DijkstraType.INT) {
                    mv.visitFieldInsn(GETSTATIC, fullPath, arrAcc.getId().getName(), "[J");
                } else if (arrayType == DijkstraType.FLOAT) {
                    mv.visitFieldInsn(GETSTATIC, fullPath, arrAcc.getId().getName(), "[D");
                } else {
                    mv.visitFieldInsn(GETSTATIC, fullPath, arrAcc.getId().getName(), "[Z");
                }
            } else if (arrAcc.getId().symbol.isGlobal() && inClass) {
                if (arrayType == DijkstraType.INT) {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, arrAcc.getId().getName(), "[J");
                } else if (arrayType == DijkstraType.FLOAT) {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, arrAcc.getId().getName(), "[D");
                } else {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, arrAcc.getId().getName(), "[Z");
                }
            } else {
                mv.visitVarInsn(ALOAD, arrAcc.getId().getAddress());
            }
            // Visit the array accessor expression so we know what index we're getting
            arrAcc.getExpression().accept(this);
            // Convert the expression type to an int
            if (arrAcc.getExpression().getType() == DijkstraType.INT) {
                mv.visitInsn(L2I);
            } else {
                mv.visitInsn(D2I);
            }
        } else if (assign.getVar() instanceof AttributeNode) {
            if (((AttributeNode) assign.getVar()).getObj().symbol.isGlobal() && !inClass) {
                mv.visitFieldInsn(GETSTATIC, fullPath, ((AttributeNode) assign.getVar()).getObj().getName(),
                        "L" + classPackage + "/"
                                + ((ObjectSymbol) ((AttributeNode) assign.getVar()).getObj().symbol).getObjectType()
                                + ";");
            } else if (((AttributeNode) assign.getVar()).getObj().symbol.isGlobal() && inClass) {
                mv.visitFieldInsn(GETFIELD, fullPath, ((AttributeNode) assign.getVar()).getObj().getName(),
                        "L" + classPackage + "/"
                                + ((ObjectSymbol) ((AttributeNode) assign.getVar()).getObj().symbol).getObjectType()
                                + ";");
            } else {
                mv.visitVarInsn(ALOAD, ((AttributeNode) assign.getVar()).getObj().getAddress());
            }
            //         mv.visitVarInsn(ALOAD, ((AttributeNode)assign.getVar()).getObj().getAddress());
        }

        assign.getExpression().accept(this); // TOS = expression value
        reading = false;
        if (assign.getType() == DijkstraType.INT) {
            if (assign.getExpression().getType() == DijkstraType.FLOAT) {
                mv.visitInsn(D2L);
            }
            assign.getVar().accept(this);
        } else if (assign.getType() == DijkstraType.FLOAT) {
            if (assign.getExpression().getType() == DijkstraType.INT) {
                mv.visitInsn(L2D);
            }
            assign.getVar().accept(this);
        } else if (assign.getType() == DijkstraType.BOOLEAN) {
            assign.getVar().accept(this);
        } else {
            assign.getVar().accept(this);
            //         mv.visitVarInsn(ASTORE, ((IDNode)assign.getVar()).getAddress());
        }
        reading = true;
        return null;
    }

    public byte[] visit(AssignListNode assignList) {
        final MethodVisitor mv = mvStack.peek();

        final List<ASTNode> ids = assignList.getVarList();
        final List<ASTNode> exprs = assignList.getExpressionList();

        for (int exprsArrayIndex = 0, idsArrayIndex = 0; exprsArrayIndex < exprs
                .size(); idsArrayIndex++, exprsArrayIndex++) {
            if (ids.get(idsArrayIndex) instanceof ArrayAccessorNode) {
                ArrayAccessorNode arrAcc = ((ArrayAccessorNode) ids.get(idsArrayIndex));
                // Load the pointer to the array
                final DijkstraType arrayType = ((ArraySymbol) arrAcc.getId().symbol).getArrayType();
                if (arrAcc.getId().symbol.isGlobal()) {
                    if (arrayType == DijkstraType.INT) {
                        mv.visitFieldInsn(GETSTATIC, fullPath, arrAcc.getId().getName(), "[J");
                    } else if (arrayType == DijkstraType.FLOAT) {
                        mv.visitFieldInsn(GETSTATIC, fullPath, arrAcc.getId().getName(), "[D");
                    } else {
                        mv.visitFieldInsn(GETSTATIC, fullPath, arrAcc.getId().getName(), "[Z");
                    }
                } else if (arrAcc.getId().symbol.isGlobal() && inClass) {
                    if (arrayType == DijkstraType.INT) {
                        mv.visitVarInsn(ALOAD, 0);
                        mv.visitFieldInsn(GETFIELD, fullPath, arrAcc.getId().getName(), "[J");
                    } else if (arrayType == DijkstraType.FLOAT) {
                        mv.visitVarInsn(ALOAD, 0);
                        mv.visitFieldInsn(GETFIELD, fullPath, arrAcc.getId().getName(), "[D");
                    } else {
                        mv.visitVarInsn(ALOAD, 0);
                        mv.visitFieldInsn(GETFIELD, fullPath, arrAcc.getId().getName(), "[Z");
                    }
                } else {
                    mv.visitVarInsn(ALOAD, arrAcc.getId().getAddress());
                }
                // Visit the array accessor expression so we know what index we're getting
                arrAcc.getExpression().accept(this);
                // Convert the expression type to an int
                if (arrAcc.getExpression().getType() == DijkstraType.INT) {
                    mv.visitInsn(L2I);
                } else {
                    mv.visitInsn(D2I);
                }
            } else if (ids.get(idsArrayIndex) instanceof AttributeNode) {
                IDNode node = ((AttributeNode) ids.get(idsArrayIndex)).getObj();
                if (node.symbol.isGlobal() && !inClass) {
                    mv.visitFieldInsn(GETSTATIC, fullPath, node.getName(),
                            "L" + classPackage + "/" + ((ObjectSymbol) node.symbol).getObjectType() + ";");
                } else if (node.symbol.isGlobal() && inClass) {
                    mv.visitFieldInsn(GETFIELD, fullPath, node.getName(),
                            "L" + classPackage + "/" + ((ObjectSymbol) node.symbol).getObjectType() + ";");
                } else {
                    mv.visitVarInsn(ALOAD, node.getAddress());
                }
                //            mv.visitVarInsn(ALOAD, ((AttributeNode)ids.get(idsArrayIndex)).getObj().getAddress());
            }

            exprs.get(exprsArrayIndex).accept(this);

            if (exprs.get(exprsArrayIndex).nodeType == ASTNodeType.FUNCALL) {
                List<DijkstraType> returnTypes = ((FunctionSymbol) ((FunctionCallNode) exprs.get(exprsArrayIndex))
                        .getId().symbol).getReturnTypes();
                Stack<Symbol> returnValues = new Stack<Symbol>();
                for (int i = returnTypes.size() - 1; i > 0; i--) {
                    returnValues.push(new Symbol(null, UNDEFINED));
                    if (returnTypes.get(i) == DijkstraType.INT) {
                        mv.visitVarInsn(LSTORE, JVMInfo.getAddressForSymbol(returnValues.peek()));
                    } else if (returnTypes.get(i) == DijkstraType.FLOAT) {
                        mv.visitVarInsn(DSTORE, JVMInfo.getAddressForSymbol(returnValues.peek()));
                    } else {
                        // boolean
                        mv.visitVarInsn(ISTORE, JVMInfo.getAddressForSymbol(returnValues.peek()));
                    }
                }
                for (int i = 1; i < returnTypes.size(); i++) {
                    idsArrayIndex++;
                    if (ids.get(idsArrayIndex) instanceof ArrayAccessorNode) {
                        ArrayAccessorNode arrAcc = ((ArrayAccessorNode) ids.get(idsArrayIndex));
                        // Load the pointer to the array
                        final DijkstraType arrayType = ((ArraySymbol) arrAcc.getId().symbol).getArrayType();
                        if (arrAcc.getId().symbol.isGlobal()) {
                            if (arrayType == DijkstraType.INT) {
                                mv.visitFieldInsn(GETSTATIC, fullPath, arrAcc.getId().getName(), "[J");
                            } else if (arrayType == DijkstraType.FLOAT) {
                                mv.visitFieldInsn(GETSTATIC, fullPath, arrAcc.getId().getName(), "[D");
                            } else {
                                mv.visitFieldInsn(GETSTATIC, fullPath, arrAcc.getId().getName(), "[Z");
                            }
                        } else if (arrAcc.getId().symbol.isGlobal() && inClass) {
                            if (arrayType == DijkstraType.INT) {
                                mv.visitVarInsn(ALOAD, 0);
                                mv.visitFieldInsn(GETFIELD, fullPath, arrAcc.getId().getName(), "[J");
                            } else if (arrayType == DijkstraType.FLOAT) {
                                mv.visitVarInsn(ALOAD, 0);
                                mv.visitFieldInsn(GETFIELD, fullPath, arrAcc.getId().getName(), "[D");
                            } else {
                                mv.visitVarInsn(ALOAD, 0);
                                mv.visitFieldInsn(GETFIELD, fullPath, arrAcc.getId().getName(), "[Z");
                            }
                        } else {
                            mv.visitVarInsn(ALOAD, arrAcc.getId().getAddress());
                        }
                        // Visit the array accessor expression so we know what index we're getting
                        arrAcc.getExpression().accept(this);
                        // Convert the expression type to an int
                        if (arrAcc.getExpression().getType() == DijkstraType.INT) {
                            mv.visitInsn(L2I);
                        } else {
                            mv.visitInsn(D2I);
                        }
                    }
                    if (returnTypes.get(i) == DijkstraType.INT) {
                        mv.visitVarInsn(LLOAD, JVMInfo.getAddressForSymbol(returnValues.peek()));
                    } else if (returnTypes.get(i) == DijkstraType.FLOAT) {
                        mv.visitVarInsn(DLOAD, JVMInfo.getAddressForSymbol(returnValues.peek()));
                    } else {
                        // boolean
                        mv.visitVarInsn(ILOAD, JVMInfo.getAddressForSymbol(returnValues.peek()));
                    }
                    returnValues.pop();
                }
            }
        }
        // Pop them off an assign them in order
        reading = false;
        final List<DijkstraType> exprTypes = getExpressionListTypes(assignList.getExpressionList());
        for (int i = ids.size() - 1; i >= 0; i--) {
            final DijkstraType idType;
            if (ids.get(i) instanceof ArrayAccessorNode) {
                idType = ((ArraySymbol) ((ArrayAccessorNode) ids.get(i)).getId().symbol).getArrayType();
            } else {
                idType = ids.get(i).getType();
            }
            DijkstraType exprType = exprTypes.get(i);
            if (idType == DijkstraType.INT) {
                if (exprType == DijkstraType.FLOAT) {
                    mv.visitInsn(D2L);
                }
            } else if (idType == DijkstraType.FLOAT) {
                if (exprType == DijkstraType.INT) {
                    mv.visitInsn(L2D);
                }
            }
            ids.get(i).accept(this);
        }
        reading = true;
        return null;
    }

    /**
     * Helper function for visit methods that deal with expression lists.
     * Takes a list of expressions and identifies the number of values it evaluates to.
     * (Expands functions with multiple returns to get the real number of values returned)
     * @param expressions The list of expressions to count
     * @return The number of values in the expression list
     */
    private List<DijkstraType> getExpressionListTypes(List<ASTNode> expressions) {
        List<DijkstraType> types = new ArrayList<DijkstraType>();
        for (ASTNode expr : expressions) {
            if (expr.nodeType == FUNCALL) {
                FunctionCallNode fd = (FunctionCallNode) expr;
                types.addAll(((FunctionSymbol) fd.getId().symbol).getReturnTypes());
            } else if (expr.nodeType == ASTNodeType.METHODCALL) {
                MethodCallNode fd = (MethodCallNode) expr;
                types.addAll(((FunctionSymbol) fd.getId().symbol).getReturnTypes());
            } else {
                types.add(expr.getType());
            }
        }
        return types;
    }

    public byte[] visit(AlternativeNode alternative) {
        final MethodVisitor mv = mvStack.peek();
        final Label endLabel = new Label();
        guardLabelStack.push(endLabel);
        visitChildren(alternative);
        guardLabelStack.pop();
        mv.visitIntInsn(BIPUSH, alternative.getLineNumber());
        mv.visitMethodInsn(INVOKESTATIC, "dijkstra/runtime/DijkstraRuntime", "abortNoAlternative", "(I)V", false);
        mv.visitLabel(endLabel);
        return null;
    }

    public byte[] visit(IterativeNode iterator) {
        final MethodVisitor mv = mvStack.peek();
        final Label startLabel = new Label();
        guardLabelStack.push(startLabel);
        mv.visitLabel(startLabel);
        visitChildren(iterator);
        guardLabelStack.pop();
        return null;
    }

    public byte[] visit(GuardNode guard) {
        final MethodVisitor mv = mvStack.peek();
        final Label failLabel = new Label();
        guard.getExpression().accept(this);
        mv.visitJumpInsn(IFEQ, failLabel);
        guard.getStatement().accept(this);
        mv.visitJumpInsn(GOTO, guardLabelStack.peek());
        mv.visitLabel(failLabel);
        return null;
    }

    /**
     * Evaluate the child expression and then negate it (logical or arithmetic). Logical
     * negation is more difficult because there is no logical negate instruction in the 
     * JVM instruction set.
     * @see dijkstra.ast.ASTVisitor#visit(dijkstra.ast.ASTNodeFactory.UnaryExpressionNode)
     */
    public byte[] visit(UnaryExpressionNode unary) {
        final MethodVisitor mv = mvStack.peek();
        visitChildren(unary); // Evaluate the expression
        // TOS = the child expression
        if (unary.type == DijkstraType.INT) {
            mv.visitInsn(LNEG);
        } else if (unary.type == DijkstraType.FLOAT) {
            mv.visitInsn(DNEG);
        } else { // Boolean ~
            final Label l1 = new Label();
            final Label l2 = new Label();
            mv.visitJumpInsn(IFEQ, l1);
            mv.visitInsn(ICONST_0); // true -> false
            mv.visitJumpInsn(GOTO, l2);
            mv.visitLabel(l1);
            mv.visitInsn(ICONST_1); // false -> true
            mv.visitLabel(l2);
        }
        return null;
    }

    /**
     * Get the values of the left and right children on the stack and then perform
     * the operation.
     * @see dijkstra.ast.ASTVisitor#visit(dijkstra.ast.ASTNodeFactory.BinaryExpressionNode)
     */
    public byte[] visit(BinaryExpressionNode binary) {
        final MethodVisitor mv = mvStack.peek();
        DijkstraType childOneType = binary.getExpr1().getType();
        if (childOneType == DijkstraType.ARRAY) {
            childOneType = ((ArraySymbol) ((ArrayAccessorNode) binary.getExpr1()).getId().symbol).getArrayType();
        }
        DijkstraType childTwoType = binary.getExpr2().getType();
        if (childTwoType == DijkstraType.ARRAY) {
            childTwoType = ((ArraySymbol) ((ArrayAccessorNode) binary.getExpr2()).getId().symbol).getArrayType();
        }
        Label lab1, lab2;
        if (binary.getOp() == DijkstraParser.AMP) {
            lab1 = new Label();
            lab2 = new Label();
            binary.getExpr1().accept(this);
            mv.visitJumpInsn(IFEQ, lab1);
            binary.getExpr2().accept(this);
            mv.visitJumpInsn(IFEQ, lab1);
            mv.visitInsn(ICONST_1);
            mv.visitJumpInsn(GOTO, lab2);
            mv.visitLabel(lab1);
            mv.visitInsn(ICONST_0);
            mv.visitLabel(lab2);
        } else if (binary.getOp() == DijkstraParser.PIPE) {
            lab1 = new Label();
            lab2 = new Label();
            binary.getExpr1().accept(this);
            mv.visitJumpInsn(IFNE, lab1);
            binary.getExpr2().accept(this);
            mv.visitJumpInsn(IFNE, lab1);
            mv.visitInsn(ICONST_0);
            mv.visitJumpInsn(GOTO, lab2);
            mv.visitLabel(lab1);
            mv.visitInsn(ICONST_1);
            mv.visitLabel(lab2);
        } else {
            binary.getExpr1().accept(this);
            if ((childOneType == DijkstraType.INT && childTwoType == DijkstraType.FLOAT)
                    || (binary.getOp() == DijkstraParser.SLASH && childOneType == DijkstraType.INT)) {
                mv.visitInsn(L2D);
            }
            binary.getExpr2().accept(this);
            if ((childOneType == DijkstraType.FLOAT && childTwoType == DijkstraType.INT)
                    || (binary.getOp() == DijkstraParser.SLASH && childTwoType == DijkstraType.INT)) {
                mv.visitInsn(L2D);
            }
            switch (binary.getOp()) {
            case DijkstraParser.LT:
                lab1 = new Label();
                lab2 = new Label();
                if (childOneType == DijkstraType.INT && childTwoType == DijkstraType.INT) {
                    mv.visitInsn(LCMP);
                } else {
                    mv.visitInsn(DCMPL);
                }
                mv.visitJumpInsn(IFGE, lab1);
                mv.visitInsn(ICONST_1); // left < right
                mv.visitJumpInsn(GOTO, lab2);
                mv.visitLabel(lab1);
                mv.visitInsn(ICONST_0); // right >= left
                mv.visitLabel(lab2);
                break;
            case DijkstraParser.LTEQ:
                lab1 = new Label();
                lab2 = new Label();
                if (childOneType == DijkstraType.INT && childTwoType == DijkstraType.INT) {
                    mv.visitInsn(LCMP);
                } else {
                    mv.visitInsn(DCMPL);
                }
                mv.visitJumpInsn(IFGT, lab1);
                mv.visitInsn(ICONST_1); // left <= right
                mv.visitJumpInsn(GOTO, lab2);
                mv.visitLabel(lab1);
                mv.visitInsn(ICONST_0); // right > left
                mv.visitLabel(lab2);
                break;
            case DijkstraParser.GT:
                lab1 = new Label();
                lab2 = new Label();
                if (childOneType == DijkstraType.INT && childTwoType == DijkstraType.INT) {
                    mv.visitInsn(LCMP);
                } else {
                    mv.visitInsn(DCMPL);
                }
                mv.visitJumpInsn(IFLE, lab1);
                mv.visitInsn(ICONST_1); // left > right
                mv.visitJumpInsn(GOTO, lab2);
                mv.visitLabel(lab1);
                mv.visitInsn(ICONST_0); // right <= left
                mv.visitLabel(lab2);
                break;
            case DijkstraParser.GTEQ:
                lab1 = new Label();
                lab2 = new Label();
                if (childOneType == DijkstraType.INT && childTwoType == DijkstraType.INT) {
                    mv.visitInsn(LCMP);
                } else {
                    mv.visitInsn(DCMPL);
                }
                mv.visitJumpInsn(IFLT, lab1);
                mv.visitInsn(ICONST_1); // left >= right
                mv.visitJumpInsn(GOTO, lab2);
                mv.visitLabel(lab1);
                mv.visitInsn(ICONST_0); // right < left
                mv.visitLabel(lab2);
                break;
            case DijkstraParser.PLUS:
                if (childOneType == DijkstraType.INT && childTwoType == DijkstraType.INT) {
                    mv.visitInsn(LADD);
                } else {
                    mv.visitInsn(DADD);
                }
                break;
            case DijkstraParser.MINUS:
                if (childOneType == DijkstraType.INT && childTwoType == DijkstraType.INT) {
                    mv.visitInsn(LSUB);
                } else {
                    mv.visitInsn(DSUB);
                }
                break;
            case DijkstraParser.STAR:
                if (childOneType == DijkstraType.INT && childTwoType == DijkstraType.INT) {
                    mv.visitInsn(LMUL);
                } else {
                    mv.visitInsn(DMUL);
                }
                break;
            case DijkstraParser.SLASH:
                mv.visitInsn(DDIV);
                break;
            case DijkstraParser.MOD:
                mv.visitInsn(LREM);
                break;
            case DijkstraParser.DIV:
                mv.visitInsn(LDIV);
                break;
            case DijkstraParser.EQ:
                lab1 = new Label();
                lab2 = new Label();
                if (childOneType == DijkstraType.INT && childTwoType == DijkstraType.INT) {
                    mv.visitInsn(LCMP);
                    mv.visitJumpInsn(IFNE, lab1);
                } else if (childOneType == DijkstraType.BOOLEAN) {
                    mv.visitJumpInsn(IF_ICMPNE, lab1);
                } else {
                    mv.visitInsn(DCMPL);
                    mv.visitJumpInsn(IFNE, lab1);
                }
                mv.visitInsn(ICONST_1);
                mv.visitJumpInsn(GOTO, lab2);
                mv.visitLabel(lab1);
                mv.visitInsn(ICONST_0);
                mv.visitLabel(lab2);
                break;
            case DijkstraParser.NEQ:
                lab1 = new Label();
                lab2 = new Label();
                if (childOneType == DijkstraType.INT && childTwoType == DijkstraType.INT) {
                    mv.visitInsn(LCMP);
                    mv.visitJumpInsn(IFEQ, lab1);
                } else if (childOneType == DijkstraType.BOOLEAN) {
                    mv.visitJumpInsn(IF_ICMPEQ, lab1);
                } else {
                    mv.visitInsn(DCMPL);
                    mv.visitJumpInsn(IFEQ, lab1);
                }
                mv.visitInsn(ICONST_1); // left != right
                mv.visitJumpInsn(GOTO, lab2);
                mv.visitLabel(lab1);
                mv.visitInsn(ICONST_0); // left = right
                mv.visitLabel(lab2);
                break;
            }
        }
        return null;
    }

    public byte[] visit(ProcedureDeclarationNode procDeclNode) {
        if (processingClassFields) {
            return null;
        }
        final String methodName = procDeclNode.getIDNode().getName();
        final StringBuilder sig = new StringBuilder();
        sig.append('(');
        for (IDNode param : procDeclNode.getParamList()) {
            if (param.getType() == DijkstraType.INT) {
                sig.append('J');
            } else if (param.getType() == DijkstraType.FLOAT) {
                sig.append('D');
            } else {
                sig.append('Z');
            }
        }
        sig.append(")V");
        if (inClass) {
            mvStack.push(cw.visitMethod(ACC_PUBLIC, methodName, sig.toString(), null, null));
        } else {
            mvStack.push(cw.visitMethod(ACC_PUBLIC + ACC_STATIC, methodName, sig.toString(), null, null));
        }
        final MethodVisitor mv = mvStack.peek();

        JVMInfo.enterScope(inClass);
        // Load the parameters into the JVMInfo so it knows the proper addresses
        for (IDNode param : procDeclNode.getParamList()) {
            param.getAddress();
        }
        mv.visitCode();
        final Label startLabel = new Label();
        mv.visitLabel(startLabel);

        procDeclNode.getCompoundNode().accept(this);

        mv.visitInsn(RETURN);
        final Label endLabel = new Label();
        mv.visitLabel(endLabel);
        int paramLoc = 0;
        for (IDNode param : procDeclNode.getParamList()) {
            if (param.getType() == DijkstraType.INT) {
                mv.visitLocalVariable(param.getName(), "J", null, startLabel, endLabel, paramLoc);
                paramLoc += 2;
            } else if (param.getType() == DijkstraType.FLOAT) {
                mv.visitLocalVariable(param.getName(), "D", null, startLabel, endLabel, paramLoc);
                paramLoc += 2;
            } else {
                mv.visitLocalVariable(param.getName(), "Z", null, startLabel, endLabel, paramLoc);
                paramLoc += 1;
            }
        }
        mv.visitMaxs(0, 0);
        mv.visitEnd();
        JVMInfo.exitScope();
        mvStack.pop();
        return null;
    }

    public byte[] visit(MethodCallNode node) {
        final MethodVisitor mv = mvStack.peek();
        final String objectType = ((ObjectSymbol) node.getObjectId().symbol).getObjectType();
        final StringBuilder sig = new StringBuilder();
        sig.append('(');
        List<Symbol> propertySymbols;
        if (node.getId().symbol.getType() == DijkstraType.PROCEDURE) {
            propertySymbols = ((ProcedureSymbol) node.getId().symbol).getParamSymbols();
        } else {
            propertySymbols = ((FunctionSymbol) node.getId().symbol).getParamSymbols();
        }
        for (Symbol property : propertySymbols) {
            if (property.getType() == DijkstraType.INT) {
                sig.append('J');
            } else if (property.getType() == DijkstraType.FLOAT) {
                sig.append('D');
            } else {
                sig.append('Z');
            }
        }
        if (node.getId().symbol.getType() == FUNCTION) {
            sig.append(")[Ljava/lang/Object;");
        } else {
            sig.append(")V");
        }

        if (node.getObjectId().symbol.isGlobal() && !inClass) {
            mv.visitFieldInsn(GETSTATIC, fullPath, node.getObjectId().getName(),
                    "L" + classPackage + "/" + ((ObjectSymbol) node.getObjectId().symbol).getObjectType() + ";");
        } else if (node.getObjectId().symbol.isGlobal() && inClass) {
            mv.visitFieldInsn(GETFIELD, fullPath, node.getObjectId().getName(),
                    "L" + classPackage + "/" + ((ObjectSymbol) node.getObjectId().symbol).getObjectType() + ";");
        } else {
            mv.visitVarInsn(ALOAD, node.getObjectId().getAddress());
        }
        int paramIndex = 0;
        for (ASTNode arg : node.getArgs()) {
            arg.accept(this);
            if (arg.nodeType == FUNCALL) {
                paramIndex += ((FunctionSymbol) ((FunctionCallNode) arg).getId().symbol).getReturnTypes().size()
                        - 1;
            } else if (propertySymbols.get(paramIndex).getType() == DijkstraType.INT
                    && arg.getType() == DijkstraType.FLOAT) {
                mv.visitInsn(D2L);
            } else if (propertySymbols.get(paramIndex).getType() == DijkstraType.FLOAT
                    && arg.getType() == DijkstraType.INT) {
                mv.visitInsn(L2D);
            }
            paramIndex++;
        }
        //      mv.visitVarInsn(ALOAD, node.getObjectId().getAddress())
        mv.visitMethodInsn(INVOKEVIRTUAL, classPackage + "/" + objectType, node.getId().getName(), sig.toString(),
                false);

        if (node.getId().symbol.getType() == FUNCTION) {
            final FunctionSymbol fSymbol = ((FunctionSymbol) node.getId().symbol);
            returnArrayStack.push(new ArraySymbol(null, UNDEFINED));
            mv.visitVarInsn(ASTORE, JVMInfo.getAddressForSymbol(returnArrayStack.peek()));
            for (int i = 0; i < fSymbol.getReturnTypes().size(); i++) {
                // Load the only element in the array and put it on the stack
                mv.visitVarInsn(ALOAD, JVMInfo.getAddressForSymbol(returnArrayStack.peek()));
                mv.visitIntInsn(SIPUSH, i);
                mv.visitInsn(AALOAD);
                DijkstraType retType = fSymbol.getReturnTypes().get(i);
                if (retType == DijkstraType.INT) {
                    mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
                } else if (retType == DijkstraType.FLOAT) {
                    mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
                } else {
                    mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
                }
            }
            returnArrayStack.pop();
        }
        return null;
    }

    public byte[] visit(ProcedureCallNode procCallNode) {
        final MethodVisitor mv = mvStack.peek();
        final String methodName = procCallNode.getId().getName();
        final StringBuilder sig = new StringBuilder();
        final List<Symbol> params = ((ProcedureSymbol) procCallNode.getId().symbol).getParamSymbols();
        final List<ASTNode> args = procCallNode.getArgs();
        sig.append('(');
        for (int i = 0; i < params.size(); i++) {
            DijkstraType paramType = params.get(i).getType();
            if (paramType == DijkstraType.INT) {
                sig.append('J');
            } else if (paramType == DijkstraType.FLOAT) {
                sig.append('D');
            } else {
                sig.append('Z');
            }
        }
        int paramIndex = 0;
        for (ASTNode arg : args) {
            arg.accept(this);
            if (arg.nodeType == FUNCALL) {
                paramIndex += ((FunctionSymbol) ((FunctionCallNode) arg).getId().symbol).getReturnTypes().size()
                        - 1;
            } else if (params.get(paramIndex).getType() == DijkstraType.INT
                    && arg.getType() == DijkstraType.FLOAT) {
                mv.visitInsn(D2L);
            } else if (params.get(paramIndex).getType() == DijkstraType.FLOAT
                    && arg.getType() == DijkstraType.INT) {
                mv.visitInsn(L2D);
            }
            paramIndex++;
        }
        sig.append(")V");
        mv.visitMethodInsn(INVOKESTATIC, classPackage + "/" + programName, methodName, sig.toString(), false);
        return null;
    }

    public byte[] visit(FunctionDeclarationNode funDeclNode) {
        if (processingClassFields) {
            return null;
        }
        final String methodName = funDeclNode.getIDNode().getName();
        final StringBuilder sig = new StringBuilder();
        sig.append('(');
        for (IDNode param : funDeclNode.getParamList()) {
            if (param.getType() == DijkstraType.INT) {
                sig.append('J');
            } else if (param.getType() == DijkstraType.FLOAT) {
                sig.append('D');
            } else {
                sig.append('Z');
            }
        }
        sig.append(')');
        sig.append("[Ljava/lang/Object;");

        if (inClass) {
            mvStack.push(cw.visitMethod(ACC_PUBLIC, methodName, sig.toString(), null, null));
        } else {
            mvStack.push(cw.visitMethod(ACC_PUBLIC + ACC_STATIC, methodName, sig.toString(), null, null));
        }
        final MethodVisitor mv = mvStack.peek();

        JVMInfo.enterScope(inClass);
        // Load the parameters into the JVMInfo so it knows the proper addresses
        for (IDNode param : funDeclNode.getParamList()) {
            param.getAddress();
        }

        mv.visitIntInsn(SIPUSH, funDeclNode.getReturnTypes().size());
        mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
        returnArrayStack.push(new ArraySymbol(null, UNDEFINED));
        mv.visitVarInsn(ASTORE, JVMInfo.getAddressForSymbol(returnArrayStack.peek()));

        mv.visitInsn(ICONST_0);
        returnIndexStack.push(new Symbol(null, UNDEFINED));
        mv.visitVarInsn(ISTORE, JVMInfo.getAddressForSymbol(returnIndexStack.peek()));

        mv.visitCode();
        final Label startLabel = new Label();
        mv.visitLabel(startLabel);

        funDeclNode.getCompoundNode().accept(this);

        returnIndexStack.pop();
        mv.visitVarInsn(ALOAD, JVMInfo.getAddressForSymbol(returnArrayStack.pop()));
        mv.visitInsn(ARETURN);
        final Label endLabel = new Label();
        mv.visitLabel(endLabel);
        int paramLoc = 0;
        for (IDNode param : funDeclNode.getParamList()) {
            if (param.getType() == DijkstraType.INT) {
                mv.visitLocalVariable(param.getName(), "J", null, startLabel, endLabel, paramLoc);
                paramLoc += 2;
            } else if (param.getType() == DijkstraType.FLOAT) {
                mv.visitLocalVariable(param.getName(), "D", null, startLabel, endLabel, paramLoc);
                paramLoc += 2;
            } else {
                mv.visitLocalVariable(param.getName(), "Z", null, startLabel, endLabel, paramLoc);
                paramLoc += 1;
            }
        }
        mv.visitMaxs(0, 0);
        mv.visitEnd();
        JVMInfo.exitScope();
        mvStack.pop();
        return null;
    }

    public byte[] visit(FunctionCallNode funCallNode) {
        final MethodVisitor mv = mvStack.peek();
        final String methodName = funCallNode.getId().getName();
        final StringBuilder sig = new StringBuilder();
        if (funCallNode.getId().symbol instanceof FunctionSymbol) {
            final FunctionSymbol fSymbol = ((FunctionSymbol) funCallNode.getId().symbol);
            final List<Symbol> params = fSymbol.getParamSymbols();
            final List<ASTNode> args = funCallNode.getArgs();
            sig.append('(');
            for (int i = 0; i < params.size(); i++) {
                DijkstraType paramType = params.get(i).getType();
                if (paramType == DijkstraType.INT) {
                    sig.append('J');
                } else if (paramType == DijkstraType.FLOAT) {
                    sig.append('D');
                } else {
                    sig.append('Z');
                }
            }
            int paramIndex = 0;
            for (ASTNode arg : args) {
                arg.accept(this);
                if (arg.nodeType == FUNCALL) {
                    paramIndex += ((FunctionSymbol) ((FunctionCallNode) arg).getId().symbol).getReturnTypes().size()
                            - 1;
                } else if (params.get(paramIndex).getType() == DijkstraType.INT
                        && arg.getType() == DijkstraType.FLOAT) {
                    mv.visitInsn(D2L);
                } else if (params.get(paramIndex).getType() == DijkstraType.FLOAT
                        && arg.getType() == DijkstraType.INT) {
                    mv.visitInsn(L2D);
                }
                paramIndex++;
            }
            sig.append(')');
            sig.append("[Ljava/lang/Object;");

            mv.visitMethodInsn(INVOKESTATIC, classPackage + "/" + programName, methodName, sig.toString(), false);
            returnArrayStack.push(new ArraySymbol(null, UNDEFINED));
            mv.visitVarInsn(ASTORE, JVMInfo.getAddressForSymbol(returnArrayStack.peek()));
            for (int i = 0; i < fSymbol.getReturnTypes().size(); i++) {
                // Load the only element in the array and put it on the stack
                mv.visitVarInsn(ALOAD, JVMInfo.getAddressForSymbol(returnArrayStack.peek()));
                mv.visitIntInsn(SIPUSH, i);
                mv.visitInsn(AALOAD);
                DijkstraType retType = fSymbol.getReturnTypes().get(i);
                if (retType == DijkstraType.INT) {
                    mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
                } else if (retType == DijkstraType.FLOAT) {
                    mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
                } else {
                    mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
                }
            }
            returnArrayStack.pop();
        } else {
            final ClassSymbol cSymbol = ((ClassSymbol) funCallNode.getId().symbol);
            final List<Symbol> properties = cSymbol.getPropertySymbols();
            final List<ASTNode> args = funCallNode.getArgs();
            mv.visitTypeInsn(NEW, classPackage + "/" + cSymbol.getId());
            mv.visitInsn(DUP);
            sig.append('(');
            for (int i = 0; i < properties.size(); i++) {
                DijkstraType paramType = properties.get(i).getType();
                if (paramType == DijkstraType.INT) {
                    sig.append('J');
                } else if (paramType == DijkstraType.FLOAT) {
                    sig.append('D');
                } else {
                    sig.append('Z');
                }
            }
            int paramIndex = 0;
            for (ASTNode arg : args) {
                arg.accept(this);
                if (arg.nodeType == FUNCALL) {
                    paramIndex += ((FunctionSymbol) ((FunctionCallNode) arg).getId().symbol).getReturnTypes().size()
                            - 1;
                } else if (properties.get(paramIndex).getType() == DijkstraType.INT
                        && arg.getType() == DijkstraType.FLOAT) {
                    mv.visitInsn(D2L);
                } else if (properties.get(paramIndex).getType() == DijkstraType.FLOAT
                        && arg.getType() == DijkstraType.INT) {
                    mv.visitInsn(L2D);
                }
                paramIndex++;
            }
            sig.append(')');
            sig.append('V');

            mv.visitMethodInsn(INVOKESPECIAL, classPackage + "/" + cSymbol.getId(), "<init>", sig.toString(),
                    false);
        }

        return null;
    }

    public byte[] visit(ReturnNode returnNode) {
        final MethodVisitor mv = mvStack.peek();
        for (ASTNode child : returnNode.children) {
            mv.visitVarInsn(ALOAD, JVMInfo.getAddressForSymbol(returnArrayStack.peek()));
            // What index are we storing it in
            int indexAddr = JVMInfo.getAddressForSymbol(returnIndexStack.peek());
            mv.visitVarInsn(ILOAD, indexAddr);
            child.accept(this);
            if (child.nodeType == FUNCALL) {
                final List<DijkstraType> returnTypes = ((FunctionSymbol) ((FunctionCallNode) child).getId().symbol)
                        .getReturnTypes();
                final Stack<Symbol> returnValues = new Stack<Symbol>();
                for (int i = returnTypes.size() - 1; i >= 0; i--) {
                    returnValues.push(new Symbol(null, UNDEFINED));
                    if (returnTypes.get(i) == DijkstraType.INT) {
                        mv.visitVarInsn(LSTORE, JVMInfo.getAddressForSymbol(returnValues.peek()));
                    } else if (returnTypes.get(i) == DijkstraType.FLOAT) {
                        mv.visitVarInsn(DSTORE, JVMInfo.getAddressForSymbol(returnValues.peek()));
                    } else {
                        // boolean
                        mv.visitVarInsn(ISTORE, JVMInfo.getAddressForSymbol(returnValues.peek()));
                    }
                }
                for (int i = 0; i < returnTypes.size(); i++) {
                    // Load the pointer to the array
                    mv.visitVarInsn(ALOAD, JVMInfo.getAddressForSymbol(returnArrayStack.peek()));
                    mv.visitIntInsn(SIPUSH, i);

                    if (returnTypes.get(i) == DijkstraType.INT) {
                        mv.visitVarInsn(LLOAD, JVMInfo.getAddressForSymbol(returnValues.peek()));
                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
                    } else if (returnTypes.get(i) == DijkstraType.FLOAT) {
                        mv.visitVarInsn(DLOAD, JVMInfo.getAddressForSymbol(returnValues.peek()));
                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;",
                                false);
                    } else {
                        // boolean
                        mv.visitVarInsn(ILOAD, JVMInfo.getAddressForSymbol(returnValues.peek()));
                        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;",
                                false);
                    }
                    mv.visitInsn(AASTORE);
                    // Increment the return index
                    mv.visitIincInsn(indexAddr, 1);
                    returnValues.pop();
                }
            } else {
                DijkstraType retType = child.getType();
                if (retType == DijkstraType.INT) {
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
                } else if (retType == DijkstraType.FLOAT) {
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
                } else {
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;",
                            false);
                }
                mv.visitInsn(AASTORE);
                // Increment the return index
                mv.visitIincInsn(indexAddr, 1);
            }
        }
        return null;
    }

    /**
     * Simply load the constant value onto the stack.
     * @see dijkstra.ast.ASTVisitor#visit(dijkstra.ast.ASTNodeFactory.ConstantNode)
     */
    public byte[] visit(ConstantNode constant) {
        final MethodVisitor mv = mvStack.peek();
        if (constant.getType() == DijkstraType.INT) {
            final Long l = Long.parseLong(constant.token.getText());
            mv.visitLdcInsn(l);
        } else if (constant.getType() == DijkstraType.FLOAT) {
            final Double d = Double.parseDouble(constant.token.getText());
            mv.visitLdcInsn(d);
        } else {
            // boolean
            if (constant.token.getText().equals("true")) {
                mv.visitInsn(ICONST_1);
            } else {
                mv.visitInsn(ICONST_0);
            }
        }
        return null;
    }

    /**
     * When visiting an ID node, we want the value of the ID on the top of stack
     * @see dijkstra.ast.ASTVisitor#visit(dijkstra.ast.ASTNodeFactory.IDNode)
     */
    public byte[] visit(IDNode id) {
        final MethodVisitor mv = mvStack.peek();
        if (reading) {
            if (id.getType() == DijkstraType.INT) {
                if (id.symbol.isGlobal() && !inClass) {
                    mv.visitFieldInsn(GETSTATIC, fullPath, id.getName(), "J");
                } else if (id.symbol.isGlobal() && inClass) {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, id.getName(), "J");
                } else {
                    mv.visitVarInsn(LLOAD, id.getAddress());
                }
            } else if (id.getType() == DijkstraType.FLOAT) {
                if (id.symbol.isGlobal() && !inClass) {
                    mv.visitFieldInsn(GETSTATIC, fullPath, id.getName(), "D");
                } else if (id.symbol.isGlobal() && inClass) {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, id.getName(), "D");
                } else {
                    mv.visitVarInsn(DLOAD, id.getAddress());
                }
            } else if (id.getType() == DijkstraType.BOOLEAN) {
                if (id.symbol.isGlobal() && !inClass) {
                    mv.visitFieldInsn(GETSTATIC, fullPath, id.getName(), "Z");
                } else if (id.symbol.isGlobal() && inClass) {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, id.getName(), "Z");
                } else {
                    mv.visitVarInsn(ILOAD, id.getAddress());
                }
            } else {
                if (id.symbol.isGlobal() && !inClass) {
                    mv.visitFieldInsn(GETSTATIC, fullPath, id.getName(),
                            "L" + classPackage + "/" + ((ObjectSymbol) id.symbol).getObjectType() + ";");
                } else if (id.symbol.isGlobal() && inClass) {
                    mv.visitVarInsn(ALOAD, 0);
                    mv.visitFieldInsn(GETFIELD, fullPath, id.getName(),
                            "L" + classPackage + "/" + ((ObjectSymbol) id.symbol).getObjectType() + ";");
                } else {
                    mv.visitVarInsn(ALOAD, id.getAddress());
                }
            }
        } else {
            if (id.getType() == DijkstraType.INT) {
                if (id.symbol.isGlobal() && !inClass) {
                    mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "J");
                } else if (id.symbol.isGlobal() && inClass) {
                    mv.visitFieldInsn(PUTFIELD, fullPath, id.getName(), "J");
                } else {
                    mv.visitVarInsn(LSTORE, id.getAddress());
                }
            } else if (id.getType() == DijkstraType.FLOAT) {
                if (id.symbol.isGlobal() && !inClass) {
                    mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "D");
                } else if (id.symbol.isGlobal() && inClass) {
                    mv.visitFieldInsn(PUTFIELD, fullPath, id.getName(), "D");
                } else {
                    mv.visitVarInsn(DSTORE, id.getAddress());
                }
            } else if (id.getType() == DijkstraType.BOOLEAN) {
                if (id.symbol.isGlobal() && !inClass) {
                    mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(), "Z");
                } else if (id.symbol.isGlobal() && inClass) {
                    mv.visitFieldInsn(PUTFIELD, fullPath, id.getName(), "Z");
                } else {
                    mv.visitVarInsn(ISTORE, id.getAddress());
                }
            } else {
                if (id.symbol.isGlobal() && !inClass) {
                    mv.visitFieldInsn(PUTSTATIC, fullPath, id.getName(),
                            "L" + classPackage + "/" + ((ObjectSymbol) id.symbol).getObjectType() + ";");
                } else if (id.symbol.isGlobal() && inClass) {
                    mv.visitFieldInsn(PUTFIELD, fullPath, id.getName(),
                            "L" + classPackage + "/" + ((ObjectSymbol) id.symbol).getObjectType() + ";");
                } else {
                    mv.visitVarInsn(ASTORE, id.getAddress());
                }
            }
        }
        return null;
    }

    //------------------------ Helper methods -----------------------------

    public byte[] getByteCode() {
        return cw.toByteArray();
    }

    /**
     * @param classPackage the classPackage to set
     */
    public void setClassPackage(String classPackage) {
        this.classPackage = classPackage;
    }
}