*/
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);
+ }
}
}
constPool.addClassInfo(type));
}
+ /**
+ * Adds a new entry of <code>exception_table</code>.
+ *
+ * @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 <code>exception_table</code>.
*/
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());
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", "<init>",
+ "(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 {
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;
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();
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)
* | postfix.expr "++" | "--"
* | postfix.expr "[" array.size "]"
* | postfix.expr "." Identifier
+ * | postfix.expr ( "[" "]" )* "." CLASS
* | postfix.expr "#" Identifier
*
* "#" is not an operator of regular Java. It separates
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 :
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;
}
}
+ /* 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
* | Identifier
* | NEW new.expr
* | "(" expression ")"
+ * | builtin.type ( "[" "]" )* "." CLASS
*
* Identifier represents either a local variable name, a member name,
* or a class name.
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);
}
}
--- /dev/null
+/*
+ * 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 <code>.class</code> notation.
+ * This is required at runtime
+ * only if <code>.class</code> 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());
+ }
+}