From 0e6ebb79d3872415b7cb3d41f9aa8f83aba623ba Mon Sep 17 00:00:00 2001 From: chiba Date: Fri, 3 Sep 2004 14:00:20 +0000 Subject: [PATCH] Now the compiler supports a switch statement. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@131 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- Readme.html | 1 + src/main/javassist/bytecode/Bytecode.java | 53 ++++++++++++- src/main/javassist/compiler/CodeGen.java | 80 +++++++++++++++++++- src/main/javassist/compiler/TypeChecker.java | 4 +- tutorial/tutorial2.html | 3 +- 5 files changed, 133 insertions(+), 8 deletions(-) diff --git a/Readme.html b/Readme.html index 035bd6bd..9b90986c 100644 --- a/Readme.html +++ b/Readme.html @@ -265,6 +265,7 @@ see javassist.Dump.
  • .class notation has been supported. The modified class file needs javassist.runtime.DotClass at runtime.
  • a bug in CtClass.getMethods() has been fixed. +
  • The compiler supports a switch statement.

    - version 3.0 beta in May 18th, 2004. diff --git a/src/main/javassist/bytecode/Bytecode.java b/src/main/javassist/bytecode/Bytecode.java index b606359e..d634ad26 100644 --- a/src/main/javassist/bytecode/Bytecode.java +++ b/src/main/javassist/bytecode/Bytecode.java @@ -298,6 +298,16 @@ public class Bytecode implements Opcode { return (v1 << 8) + (v2 & 0xff); } + /** + * Reads a signed 32bit value at the offset from the beginning of the + * bytecode sequence. + */ + public int read32bit(int offset) { + int v1 = read16bit(offset); + int v2 = read16bit(offset + 2); + return (v1 << 16) + (v2 & 0xffff); + } + /** * Writes an 8bit value at the offset from the beginning of the * bytecode sequence. @@ -321,10 +331,19 @@ public class Bytecode implements Opcode { * bytecode sequence. */ public void write16bit(int offset, int value) { - write(offset, value >>> 8); + write(offset, value >> 8); write(offset + 1, value); } + /** + * Writes an 32bit value at the offset from the beginning of the + * bytecode sequence. + */ + public void write32bit(int offset, int value) { + write16bit(offset, value >> 16); + write16bit(offset + 2, value); + } + /** * Appends an 8bit value to the end of the bytecode sequence. */ @@ -339,6 +358,38 @@ public class Bytecode implements Opcode { } } + /** + * Appends a 32bit value to the end of the bytecode sequence. + */ + public void add32bit(int value) { + add(value >> 24); + add(value >> 16); + add(value >> 8); + add(value); + } + + /** + * Appends the length-byte gap to the end of the bytecode sequence. + * + * @param length the gap length in byte. + */ + public void addGap(int length) { + if (num < bufsize) { + num += length; + if (num <= bufsize) + return; + else { + length = num - bufsize; + num = bufsize; + } + } + + if (next == null) + next = new Bytecode(); + + next.addGap(length); + } + /** * Appends an 8bit opcode to the end of the bytecode sequence. * The current stack depth is updated. diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java index bf6c97ef..221cbfcd 100644 --- a/src/main/javassist/compiler/CodeGen.java +++ b/src/main/javassist/compiler/CodeGen.java @@ -16,6 +16,7 @@ package javassist.compiler; import java.util.ArrayList; +import java.util.Arrays; import javassist.compiler.ast.*; import javassist.bytecode.*; @@ -338,14 +339,12 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { atThrowStmnt(st); else if (op == TRY) atTryStmnt(st); + else if (op == SWITCH) + atSwitchStmnt(st); else if (op == SYNCHRONIZED) { hasReturned = false; throw new CompileError("sorry, synchronized is not supported"); } - else if (op == SWITCH) { - hasReturned = false; - throw new CompileError("sorry, switch is not supported"); - } else { // LABEL, SWITCH label stament might be null?. hasReturned = false; @@ -474,6 +473,79 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { hasReturned = false; } + private void atSwitchStmnt(Stmnt st) throws CompileError { + compileExpr(st.head()); + + ArrayList prevBreakList = breakList; + breakList = new ArrayList(); + int opcodePc = bytecode.currentPc(); + bytecode.addOpcode(LOOKUPSWITCH); + int npads = 3 - (opcodePc & 3); + while (npads-- > 0) + bytecode.add(0); + + Stmnt body = (Stmnt)st.tail(); + int npairs = 0; + for (ASTList list = body; list != null; list = list.tail()) + if (((Stmnt)list.head()).getOperator() == CASE) + ++npairs; + + // opcodePc2 is the position at which the default jump offset is. + int opcodePc2 = bytecode.currentPc(); + bytecode.addGap(4); + bytecode.add32bit(npairs); + bytecode.addGap(npairs * 8); + + long[] pairs = new long[npairs]; + int ipairs = 0; + int defaultPc = -1; + for (ASTList list = body; list != null; list = list.tail()) { + Stmnt label = (Stmnt)list.head(); + int op = label.getOperator(); + if (op == DEFAULT) + defaultPc = bytecode.currentPc(); + else if (op != CASE) + fatal(); + else { + pairs[ipairs++] + = ((long)computeLabel(label.head()) << 32) + + ((long)(bytecode.currentPc() - opcodePc) & 0xffffffff); + } + + hasReturned = false; + ((Stmnt)label.tail()).accept(this); + } + + Arrays.sort(pairs); + int pc = opcodePc2 + 8; + for (int i = 0; i < npairs; ++i) { + bytecode.write32bit(pc, (int)(pairs[i] >>> 32)); + bytecode.write32bit(pc + 4, (int)pairs[i]); + pc += 8; + } + + if (defaultPc < 0 || breakList.size() > 0) + hasReturned = false; + + int endPc = bytecode.currentPc(); + if (defaultPc < 0) + defaultPc = endPc; + + bytecode.write32bit(opcodePc2, defaultPc - opcodePc); + + patchGoto(breakList, endPc); + breakList = prevBreakList; + } + + private int computeLabel(ASTree expr) throws CompileError { + doTypeCheck(expr); + expr = TypeChecker.stripPlusExpr(expr); + if (expr instanceof IntConst) + return (int)((IntConst)expr).get(); + else + throw new CompileError("bad case label"); + } + private void atBreakStmnt(Stmnt st, boolean notCont) throws CompileError { diff --git a/src/main/javassist/compiler/TypeChecker.java b/src/main/javassist/compiler/TypeChecker.java index 9b505afb..3ce286cf 100644 --- a/src/main/javassist/compiler/TypeChecker.java +++ b/src/main/javassist/compiler/TypeChecker.java @@ -347,7 +347,9 @@ public class TypeChecker extends Visitor implements Opcode, TokenId { } } - private static ASTree stripPlusExpr(ASTree expr) { + /* CodeGen.atSwitchStmnt() also calls stripPlusExpr(). + */ + static ASTree stripPlusExpr(ASTree expr) { if (expr instanceof BinExpr) { BinExpr e = (BinExpr)expr; if (e.getOperator() == '+' && e.oprand2() == null) diff --git a/tutorial/tutorial2.html b/tutorial/tutorial2.html index 9c158377..778f44ee 100644 --- a/tutorial/tutorial2.html +++ b/tutorial/tutorial2.html @@ -1395,8 +1395,7 @@ supported.

  • Inner classes or anonymous classes are not supported. -

  • switch or synchronized -statements are not supported yet. +

  • synchronized statements are not supported yet.

  • Labeled continue and break statements are not supported. -- 2.39.5