]> source.dussan.org Git - javassist.git/commitdiff
.class support
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Mon, 16 Aug 2004 15:31:30 +0000 (15:31 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Mon, 16 Aug 2004 15:31:30 +0000 (15:31 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@123 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

src/main/javassist/CannotCompileException.java
src/main/javassist/bytecode/Bytecode.java
src/main/javassist/compiler/CodeGen.java
src/main/javassist/compiler/MemberCodeGen.java
src/main/javassist/compiler/Parser.java
src/main/javassist/runtime/DotClass.java [new file with mode: 0644]

index 6bb5c038b5ef587ca32cbd6533ae267499084304..f01a79a0896a73b57c07904fd088102b7b51169e 100644 (file)
@@ -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);
+        }
     }
 }
index e319eeea8574c6909eb51041222db8be8f0e2db8..b606359e36becd04c88084b7ea141548e4cf3208 100644 (file)
@@ -234,6 +234,17 @@ public class Bytecode implements Opcode {
                             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>.
      */
index 5878d2b63a640ae8f456a0ece918b1eacff78d8b..35d068177d334c688373302cdcd41980cd3ac03c 100644 (file)
@@ -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", "<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 {
index a70acf84efc370e5a29247f8c3384a9cdc92bee5..200a0033278e16740ebc5c48baca94872f431eae 100644 (file)
@@ -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;
index 62de6bccd33d47f92d1f3fc9c9cc8fb4a4fdcb9e..2286905ff0694af9620ce0fac194d3e03742695e 100644 (file)
@@ -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 (file)
index 0000000..2c4ded9
--- /dev/null
@@ -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 <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());
+    }
+}