From: chiba Date: Mon, 16 Aug 2004 15:31:30 +0000 (+0000) Subject: .class support X-Git-Tag: rel_3_17_1_ga~490 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=7fd8dd68388f6e910b1e0f54df363a84481d0809;p=javassist.git .class support git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@123 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- diff --git a/src/main/javassist/CannotCompileException.java b/src/main/javassist/CannotCompileException.java index 6bb5c038..f01a79a0 100644 --- a/src/main/javassist/CannotCompileException.java +++ b/src/main/javassist/CannotCompileException.java @@ -90,7 +90,9 @@ public class CannotCompileException extends Exception { */ public void printStackTrace(java.io.PrintWriter w) { super.printStackTrace(w); - w.println("Caused by:"); - cause.printStackTrace(w); + if (cause != null) { + w.println("Caused by:"); + cause.printStackTrace(w); + } } } diff --git a/src/main/javassist/bytecode/Bytecode.java b/src/main/javassist/bytecode/Bytecode.java index e319eeea..b606359e 100644 --- a/src/main/javassist/bytecode/Bytecode.java +++ b/src/main/javassist/bytecode/Bytecode.java @@ -234,6 +234,17 @@ public class Bytecode implements Opcode { constPool.addClassInfo(type)); } + /** + * Adds a new entry of exception_table. + * + * @param type the fully-qualified name of a throwable class. + */ + public void addExceptionHandler(int start, int end, + int handler, String type) { + addExceptionHandler(start, end, handler, + constPool.addClassInfo(type)); + } + /** * Adds a new entry of exception_table. */ diff --git a/src/main/javassist/compiler/CodeGen.java b/src/main/javassist/compiler/CodeGen.java index 5878d2b6..35d06817 100644 --- a/src/main/javassist/compiler/CodeGen.java +++ b/src/main/javassist/compiler/CodeGen.java @@ -1279,11 +1279,11 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { atFieldRead(expr); } else if (token == MEMBER) { // field read - String member = ((Symbol)expr.oprand2()).get(); - if (member.equals("class")) - atClassObject(expr); // .class - else - atFieldRead(expr); + /* MEMBER ('#') is an extension by Javassist. + * The compiler internally uses # for compiling .class + * expressions such as "int.class". + */ + atFieldRead(expr); } else if (token == ARRAY) atArrayRead(oprand, expr.oprand2()); @@ -1354,14 +1354,73 @@ public abstract class CodeGen extends Visitor implements Opcode, TokenId { public void atClassObject(Expr expr) throws CompileError { ASTree op1 = expr.oprand1(); - String cname; - if (op1 instanceof ASTList) - cname = Declarator.astToClassName((ASTList)op1, '/'); - else - cname = ((Symbol)op1).get(); + if (!(op1 instanceof Symbol)) + throw new CompileError("fatal error: badly parsed .class expr"); + + String cname = ((Symbol)op1).get(); + if (cname.startsWith("[")) { + int i = cname.indexOf("[L"); + if (i >= 0) { + String name = cname.substring(i + 2, cname.length() - 1); + String name2 = resolveClassName(name); + if (!name.equals(name2)) { + /* For example, to obtain String[].class, + * "[Ljava.lang.String;" (not "[Ljava/lang/String"!) + * must be passed to Class.forName(). + */ + name2 = MemberResolver.jvmToJavaName(name2); + StringBuffer sbuf = new StringBuffer(); + while (i-- >= 0) + sbuf.append('['); + + sbuf.append('L').append(name2).append(';'); + cname = sbuf.toString(); + } + } + } + else { + cname = resolveClassName(MemberResolver.javaToJvmName(cname)); + cname = MemberResolver.jvmToJavaName(cname); + } - cname = resolveClassName(cname); - throw new CompileError(".class is not supported: " + cname); + int start = bytecode.currentPc(); + bytecode.addLdc(cname); + bytecode.addInvokestatic("java.lang.Class", "forName", + "(Ljava/lang/String;)Ljava/lang/Class;"); + int end = bytecode.currentPc(); + bytecode.addOpcode(Opcode.GOTO); + int pc = bytecode.currentPc(); + bytecode.addIndex(0); // correct later + + bytecode.addExceptionHandler(start, end, bytecode.currentPc(), + "java.lang.ClassNotFoundException"); + + /* -- the following code is for inlining a call to DotClass.fail(). + + int var = getMaxLocals(); + incMaxLocals(1); + bytecode.growStack(1); + bytecode.addAstore(var); + + bytecode.addNew("java.lang.NoClassDefFoundError"); + bytecode.addOpcode(DUP); + bytecode.addAload(var); + bytecode.addInvokevirtual("java.lang.ClassNotFoundException", + "getMessage", "()Ljava/lang/String;"); + bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "", + "(Ljava/lang/String;)V"); + */ + + bytecode.growStack(1); + bytecode.addInvokestatic("javassist.runtime.DotClass", "fail", + "(Ljava/lang/ClassNotFoundException;)" + + "Ljava/lang/NoClassDefFoundError;"); + bytecode.addOpcode(ATHROW); + bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); + + exprType = CLASS; + arrayDim = 0; + className = "java/lang/Class"; } public void atArrayLength(Expr expr) throws CompileError { diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index a70acf84..200a0033 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -776,7 +776,10 @@ public class MemberCodeGen extends CodeGen { Expr e = (Expr)expr; int op = e.getOperator(); if (op == MEMBER) { - // static member by # (extension by Javassist) + /* static member by # (extension by Javassist) + * For example, if int.class is parsed, the resulting tree + * is (# "java.lang.Integer" "TYPE"). + */ CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(), (Symbol)e.oprand2()); resultStatic = true; diff --git a/src/main/javassist/compiler/Parser.java b/src/main/javassist/compiler/Parser.java index 62de6bcc..2286905f 100644 --- a/src/main/javassist/compiler/Parser.java +++ b/src/main/javassist/compiler/Parser.java @@ -865,10 +865,12 @@ public final class Parser implements TokenId { unary.expr2 is a unary.expr begining with "(", NULL, StringL, Identifier, THIS, SUPER, or NEW. + + Either "(int.class)" or "(String[].class)" is a not cast expression. */ private ASTree parseCast(SymbolTable tbl) throws CompileError { int t = lex.lookAhead(1); - if (isBuiltinType(t)) { + if (isBuiltinType(t) && nextIsBuiltinCast()) { lex.get(); // '(' lex.get(); // primitive type int dim = parseArrayDimension(); @@ -890,6 +892,16 @@ public final class Parser implements TokenId { return parsePostfix(tbl); } + private boolean nextIsBuiltinCast() { + int t; + int i = 2; + while ((t = lex.lookAhead(i++)) == '[') + if (lex.lookAhead(i++) != ']') + return false; + + return lex.lookAhead(i - 1) == ')'; + } + private boolean nextIsClassCast() { int i = nextIsClassType(1); if (i < 0) @@ -958,6 +970,7 @@ public final class Parser implements TokenId { * | postfix.expr "++" | "--" * | postfix.expr "[" array.size "]" * | postfix.expr "." Identifier + * | postfix.expr ( "[" "]" )* "." CLASS * | postfix.expr "#" Identifier * * "#" is not an operator of regular Java. It separates @@ -993,11 +1006,20 @@ public final class Parser implements TokenId { expr = parseMethodCall(tbl, expr); break; case '[' : - index = parseArrayIndex(tbl); - if (index == null) - throw new SyntaxError(lex); + if (lex.lookAhead(1) == ']') { + int dim = parseArrayDimension(); + if (lex.get() != '.' || lex.get() != CLASS) + throw new SyntaxError(lex); - expr = Expr.make(ARRAY, expr, index); + expr = parseDotClass(expr, dim); + } + else { + index = parseArrayIndex(tbl); + if (index == null) + throw new SyntaxError(lex); + + expr = Expr.make(ARRAY, expr, index); + } break; case PLUSPLUS : case MINUSMINUS : @@ -1007,25 +1029,23 @@ public final class Parser implements TokenId { case '.' : lex.get(); t = lex.get(); - if (t == CLASS) - str = "class"; - else if (t == Identifier) + if (t == CLASS) { + expr = parseDotClass(expr, 0); + } + else if (t == Identifier) { str = lex.getString(); + expr = Expr.make('.', expr, new Member(str)); + } else throw new CompileError("missing member name", lex); - - expr = Expr.make('.', expr, new Member(str)); break; case '#' : lex.get(); t = lex.get(); - if (t == CLASS) - str = "class"; - else if (t == Identifier) - str = lex.getString(); - else + if (t != Identifier) throw new CompileError("missing static member name", lex); + str = lex.getString(); expr = Expr.make(MEMBER, new Symbol(toClassName(expr)), new Member(str)); break; @@ -1035,6 +1055,76 @@ public final class Parser implements TokenId { } } + /* Parse a .class expression on a class type. For example, + * String.class => ('.' "String" "class") + * String[].class => ('.' "[LString;" "class") + */ + private ASTree parseDotClass(ASTree className, int dim) + throws CompileError + { + String cname = toClassName(className); + if (dim > 0) { + StringBuffer sbuf = new StringBuffer(); + while (dim-- > 0) + sbuf.append('['); + + sbuf.append('L').append(cname.replace('.', '/')).append(';'); + cname = sbuf.toString(); + } + + return Expr.make('.', new Symbol(cname), new Member("class")); + } + + /* Parses a .class expression on a built-in type. For example, + * int.class => ('#' "java.lang.Integer" "TYPE") + * int[].class => ('.' "[I", "class") + */ + private ASTree parseDotClass(int builtinType, int dim) + throws CompileError + { + if (dim > 0) { + String cname = CodeGen.toJvmTypeName(builtinType, dim); + return Expr.make('.', new Symbol(cname), new Member("class")); + } + else { + String cname; + switch(builtinType) { + case BOOLEAN : + cname = "java.lang.Boolean"; + break; + case BYTE : + cname = "java.lang.Byte"; + break; + case CHAR : + cname = "java.lang.Character"; + break; + case SHORT : + cname = "java.lang.Short"; + break; + case INT : + cname = "java.lang.Integer"; + break; + case LONG : + cname = "java.lang.Long"; + break; + case FLOAT : + cname = "java.lang.Float"; + break; + case DOUBLE : + cname = "java.lang.Double"; + break; + case VOID : + cname = "java.lang.Void"; + break; + default : + throw new CompileError("invalid builtin type: " + + builtinType); + } + + return Expr.make(MEMBER, new Symbol(cname), new Member("TYPE")); + } + } + /* method.call : method.expr "(" argument.list ")" * method.expr : THIS | SUPER | Identifier * | postfix.expr "." Identifier @@ -1092,6 +1182,7 @@ public final class Parser implements TokenId { * | Identifier * | NEW new.expr * | "(" expression ")" + * | builtin.type ( "[" "]" )* "." CLASS * * Identifier represents either a local variable name, a member name, * or a class name. @@ -1127,6 +1218,12 @@ public final class Parser implements TokenId { else throw new CompileError(") is missing", lex); default : + if (isBuiltinType(t) || t == VOID) { + int dim = parseArrayDimension(); + if (lex.get() == '.' && lex.get() == CLASS) + return parseDotClass(t, dim); + } + throw new SyntaxError(lex); } } diff --git a/src/main/javassist/runtime/DotClass.java b/src/main/javassist/runtime/DotClass.java new file mode 100644 index 00000000..2c4ded97 --- /dev/null +++ b/src/main/javassist/runtime/DotClass.java @@ -0,0 +1,28 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2004 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.runtime; + +/** + * A support class for implementing .class notation. + * This is required at runtime + * only if .class notation is used in source code given + * to the Javassist compiler. + */ +public class DotClass { + public static NoClassDefFoundError fail(ClassNotFoundException e) { + return new NoClassDefFoundError(e.getMessage()); + } +}