📄 codegen.java
字号:
/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */package javassist.compiler;import java.util.ArrayList;import java.util.Arrays;import javassist.compiler.ast.*;import javassist.bytecode.*;/* The code generator is implemeted by three files: * CodeGen.java, MemberCodeGen.java, and JvstCodeGen. * I just wanted to split a big file into three smaller ones. */public abstract class CodeGen extends Visitor implements Opcode, TokenId { static final String javaLangObject = "java.lang.Object"; static final String jvmJavaLangObject = "java/lang/Object"; static final String javaLangString = "java.lang.String"; static final String jvmJavaLangString = "java/lang/String"; protected Bytecode bytecode; private int tempVar; TypeChecker typeChecker; /** * true if the last visited node is a return statement. */ protected boolean hasReturned; /** * Must be true if compilation is for a static method. */ public boolean inStaticMethod; protected ArrayList breakList, continueList; /** * doit() in ReturnHook is called from atReturn(). */ protected static abstract class ReturnHook { ReturnHook next; protected abstract void doit(Bytecode b, int opcode); protected ReturnHook(CodeGen gen) { next = gen.returnHooks; gen.returnHooks = this; } protected void remove(CodeGen gen) { gen.returnHooks = next; } } protected ReturnHook returnHooks; /* The following fields are used by atXXX() methods * for returning the type of the compiled expression. */ protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ... protected int arrayDim; protected String className; // JVM-internal representation public CodeGen(Bytecode b) { bytecode = b; tempVar = -1; typeChecker = null; hasReturned = false; inStaticMethod = false; breakList = null; continueList = null; returnHooks = null; } public void setTypeChecker(TypeChecker checker) { typeChecker = checker; } protected static void fatal() throws CompileError { throw new CompileError("fatal"); } public static boolean is2word(int type, int dim) { return dim == 0 && (type == DOUBLE || type == LONG); } public int getMaxLocals() { return bytecode.getMaxLocals(); } public void setMaxLocals(int n) { bytecode.setMaxLocals(n); } protected void incMaxLocals(int size) { bytecode.incMaxLocals(size); } /** * Returns a local variable that single or double words can be * stored in. */ protected int getTempVar() { if (tempVar < 0) { tempVar = getMaxLocals(); incMaxLocals(2); } return tempVar; } protected int getLocalVar(Declarator d) { int v = d.getLocalVar(); if (v < 0) { v = getMaxLocals(); // delayed variable allocation. d.setLocalVar(v); incMaxLocals(1); } return v; } /** * Returns the JVM-internal representation of this class name. */ protected abstract String getThisName(); /** * Returns the JVM-internal representation of this super class name. */ protected abstract String getSuperName() throws CompileError; /* Converts a class name into a JVM-internal representation. * * It may also expand a simple class name to java.lang.*. * For example, this converts Object into java/lang/Object. */ protected abstract String resolveClassName(ASTList name) throws CompileError; /* Expands a simple class name to java.lang.*. * For example, this converts Object into java/lang/Object. */ protected abstract String resolveClassName(String jvmClassName) throws CompileError; /** * @param name the JVM-internal representation. * name is not exapnded to java.lang.*. */ protected static String toJvmArrayName(String name, int dim) { if (name == null) return null; if (dim == 0) return name; else { StringBuffer sbuf = new StringBuffer(); int d = dim; while (d-- > 0) sbuf.append('['); sbuf.append('L'); sbuf.append(name); sbuf.append(';'); return sbuf.toString(); } } protected static String toJvmTypeName(int type, int dim) { char c = 'I'; switch(type) { case BOOLEAN : c = 'Z'; break; case BYTE : c = 'B'; break; case CHAR : c = 'C'; break; case SHORT : c = 'S'; break; case INT : c = 'I'; break; case LONG : c = 'J'; break; case FLOAT : c = 'F'; break; case DOUBLE : c = 'D'; break; case VOID : c = 'V'; break; } StringBuffer sbuf = new StringBuffer(); while (dim-- > 0) sbuf.append('['); sbuf.append(c); return sbuf.toString(); } public void compileExpr(ASTree expr) throws CompileError { doTypeCheck(expr); expr.accept(this); } public boolean compileBooleanExpr(boolean branchIf, ASTree expr) throws CompileError { doTypeCheck(expr); return booleanExpr(branchIf, expr); } public void doTypeCheck(ASTree expr) throws CompileError { if (typeChecker != null) expr.accept(typeChecker); } public void atASTList(ASTList n) throws CompileError { fatal(); } public void atPair(Pair n) throws CompileError { fatal(); } public void atSymbol(Symbol n) throws CompileError { fatal(); } public void atFieldDecl(FieldDecl field) throws CompileError { field.getInit().accept(this); } public void atMethodDecl(MethodDecl method) throws CompileError { ASTList mods = method.getModifiers(); setMaxLocals(1); while (mods != null) { Keyword k = (Keyword)mods.head(); mods = mods.tail(); if (k.get() == STATIC) { setMaxLocals(0); inStaticMethod = true; } } ASTList params = method.getParams(); while (params != null) { atDeclarator((Declarator)params.head()); params = params.tail(); } Stmnt s = method.getBody(); atMethodBody(s, method.isConstructor(), method.getReturn().getType() == VOID); } /** * @param isCons true if super() must be called. * false if the method is a class initializer. */ public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid) throws CompileError { if (s == null) return; if (isCons && needsSuperCall(s)) insertDefaultSuperCall(); hasReturned = false; s.accept(this); if (!hasReturned) if (isVoid) { bytecode.addOpcode(Opcode.RETURN); hasReturned = true; } else throw new CompileError("no return statement"); } private boolean needsSuperCall(Stmnt body) throws CompileError { if (body.getOperator() == BLOCK) body = (Stmnt)body.head(); if (body != null && body.getOperator() == EXPR) { ASTree expr = body.head(); if (expr != null && expr instanceof Expr && ((Expr)expr).getOperator() == CALL) { ASTree target = ((Expr)expr).head(); if (target instanceof Keyword) { int token = ((Keyword)target).get(); return token != THIS && token != SUPER; } } } return true; } protected abstract void insertDefaultSuperCall() throws CompileError; public void atStmnt(Stmnt st) throws CompileError { if (st == null) return; // empty int op = st.getOperator(); if (op == EXPR) { ASTree expr = st.getLeft(); doTypeCheck(expr); if (expr instanceof AssignExpr) atAssignExpr((AssignExpr)expr, false); else if (isPlusPlusExpr(expr)) { Expr e = (Expr)expr; atPlusPlus(e.getOperator(), e.oprand1(), e, false); } else { expr.accept(this); if (is2word(exprType, arrayDim)) bytecode.addOpcode(POP2); else if (exprType != VOID) bytecode.addOpcode(POP); } } else if (op == DECL || op == BLOCK) { ASTList list = st; while (list != null) { ASTree h = list.head(); list = list.tail(); if (h != null) h.accept(this); } } else if (op == IF) atIfStmnt(st); else if (op == WHILE || op == DO) atWhileStmnt(st, op == WHILE); else if (op == FOR) atForStmnt(st); else if (op == BREAK || op == CONTINUE) atBreakStmnt(st, op == BREAK); else if (op == TokenId.RETURN) atReturnStmnt(st); else if (op == THROW) atThrowStmnt(st); else if (op == TRY) atTryStmnt(st); else if (op == SWITCH) atSwitchStmnt(st); else if (op == SYNCHRONIZED) atSyncStmnt(st); else { // LABEL, SWITCH label stament might be null?. hasReturned = false; throw new CompileError( "sorry, not supported statement: TokenId " + op); } } private void atIfStmnt(Stmnt st) throws CompileError { ASTree expr = st.head(); Stmnt thenp = (Stmnt)st.tail().head(); Stmnt elsep = (Stmnt)st.tail().tail().head(); compileBooleanExpr(false, expr); int pc = bytecode.currentPc(); int pc2 = 0; bytecode.addIndex(0); // correct later hasReturned = false; if (thenp != null) thenp.accept(this); boolean thenHasReturned = hasReturned; hasReturned = false; if (elsep != null && !thenHasReturned) { bytecode.addOpcode(Opcode.GOTO); pc2 = bytecode.currentPc(); bytecode.addIndex(0); } bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); if (elsep != null) { elsep.accept(this); if (!thenHasReturned) bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); hasReturned = thenHasReturned && hasReturned; } } private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError { ArrayList prevBreakList = breakList; ArrayList prevContList = continueList; breakList = new ArrayList(); continueList = new ArrayList(); ASTree expr = st.head(); Stmnt body = (Stmnt)st.tail(); int pc = 0; if (notDo) { bytecode.addOpcode(Opcode.GOTO); pc = bytecode.currentPc(); bytecode.addIndex(0); } int pc2 = bytecode.currentPc(); if (body != null) body.accept(this); int pc3 = bytecode.currentPc(); if (notDo) bytecode.write16bit(pc, pc3 - pc + 1); boolean alwaysBranch = compileBooleanExpr(true, expr); bytecode.addIndex(pc2 - bytecode.currentPc() + 1); patchGoto(breakList, bytecode.currentPc()); patchGoto(continueList, pc3); continueList = prevContList; breakList = prevBreakList; hasReturned = alwaysBranch; } protected void patchGoto(ArrayList list, int targetPc) { int n = list.size(); for (int i = 0; i < n; ++i) { int pc = ((Integer)list.get(i)).intValue(); bytecode.write16bit(pc, targetPc - pc + 1); } } private void atForStmnt(Stmnt st) throws CompileError { ArrayList prevBreakList = breakList; ArrayList prevContList = continueList; breakList = new ArrayList(); continueList = new ArrayList(); Stmnt init = (Stmnt)st.head(); ASTList p = st.tail(); ASTree expr = p.head(); p = p.tail(); Stmnt update = (Stmnt)p.head(); Stmnt body = (Stmnt)p.tail(); if (init != null) init.accept(this); int pc = bytecode.currentPc(); int pc2 = 0; if (expr != null) { compileBooleanExpr(false, expr); pc2 = bytecode.currentPc(); bytecode.addIndex(0); } if (body != null) body.accept(this);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -