View Javadoc

1   package serp.bytecode;
2   
3   import java.util.*;
4   
5   import serp.bytecode.lowlevel.*;
6   
7   /***
8    * Pseudo-instruction used to place {@link Class} objects onto the stack.
9    * This logical instruction may actually involve a large chunk of code, and
10   * may even add static synthetic fields and methods to the owning class.
11   * Therefore, once the type of class being loaded is set, it cannot
12   * be changed. Also, this instruction is invalid as the target of
13   * any jump instruction or exception handler.
14   *
15   * @author Abe White
16   */
17  public class ClassConstantInstruction {
18      private static final Class[] _params = new Class[] { String.class };
19      private static final Map _wrappers = new HashMap();
20      static {
21          _wrappers.put(byte.class.getName(), Byte.class);
22          _wrappers.put(boolean.class.getName(), Boolean.class);
23          _wrappers.put(char.class.getName(), Character.class);
24          _wrappers.put(double.class.getName(), Double.class);
25          _wrappers.put(float.class.getName(), Float.class);
26          _wrappers.put(int.class.getName(), Integer.class);
27          _wrappers.put(long.class.getName(), Long.class);
28          _wrappers.put(short.class.getName(), Short.class);
29      }
30  
31      private Instruction _ins = null;
32      private Code _code = null;
33      private BCClass _class = null;
34      private boolean _invalid = false;
35  
36      ClassConstantInstruction(BCClass bc, Code code, Instruction nop) {
37          _class = bc;
38          _code = code;
39          _ins = nop;
40      }
41  
42      /***
43       * Set the type of class being loaded.
44       *
45       * @return the first Instruction of the block added by setting the type
46       * @throws IllegalStateException if type has already been set
47       */
48      public Instruction setClass(String name) {
49          name = _class.getProject().getNameCache().getExternalForm(name, false);
50          setClassName(name, getWrapperClass(name));
51          return _ins;
52      }
53  
54      /***
55       * Set the type of class being loaded.
56       *
57       * @return the first Instruction of the block added by setting the type
58       * @throws IllegalStateException if type has already been set
59       */
60      public Instruction setClass(Class type) {
61          return setClass(type.getName());
62      }
63  
64      /***
65       * Set the type of class being loaded.
66       *
67       * @return the first Instruction of the block added by setting the type
68       * @throws IllegalStateException if type has already been set
69       */
70      public Instruction setClass(BCClass type) {
71          return setClass(type.getName());
72      }
73  
74      /***
75       * Set the name of the class to load.
76       */
77      private void setClassName(String name, Class wrapper) {
78          if (_invalid)
79              throw new IllegalStateException();
80  
81          // remember the position of the code iterator
82          Instruction before = (_code.hasNext()) ? _code.next() : null;
83          _code.before(_ins);
84          _code.next();
85          if (wrapper != null)
86              _code.getstatic().setField(wrapper, "TYPE", Class.class);
87          else
88              setObject(name);
89  
90          // move to the old position
91          if (before != null)
92              _code.before(before);
93          else
94              _code.afterLast();
95          _invalid = true;
96      }
97  
98      /***
99       * Adds fields and methods as necessary to load a class constant of
100      * an object type.
101      */
102     private void setObject(String name) {
103         BCField field = addClassField(name);
104         BCMethod method = addClassLoadMethod();
105 
106         // copied from the way jikes loads classes
107         _code.getstatic().setField(field);
108         JumpInstruction ifnull = _code.ifnull();
109         _code.getstatic().setField(field);
110         JumpInstruction go2 = _code.go2();
111         ifnull.setTarget(_code.constant().setValue(name));
112         _code.invokestatic().setMethod(method);
113         _code.dup();
114         _code.putstatic().setField(field);
115         go2.setTarget(_code.nop());
116     }
117 
118     /***
119      * Adds a static field to hold the loaded class constant.
120      */
121     private BCField addClassField(String name) {
122         String fieldName = "class$L" 
123             + name.replace('.', '$').replace('[', '$').replace(';', '$');
124         BCField field = _class.getDeclaredField(fieldName);
125         if (field == null) {
126             field = _class.declareField(fieldName, Class.class);
127             field.makePackage();
128             field.setStatic(true);
129             field.setSynthetic(true);
130         }
131         return field;
132     }
133 
134     /***
135      * Adds the standard <code>class$<code> method used inernally by classes
136      * to load class constants for object types.
137      */
138     private BCMethod addClassLoadMethod() {
139         BCMethod method = _class.getDeclaredMethod("class$", _params);
140         if (method != null)
141             return method;
142 
143         // add the special synthetic method
144         method = _class.declareMethod("class$", Class.class, _params);
145         method.setStatic(true);
146         method.makePackage();
147         method.setSynthetic(true);
148 
149         // copied directly from the output of the jikes compiler
150         Code code = method.getCode(true);
151         code.setMaxStack(3);
152         code.setMaxLocals(2);
153 
154         Instruction tryStart = code.aload().setLocal(0);
155         code.invokestatic().setMethod(Class.class, "forName", Class.class, 
156             _params);
157         Instruction tryEnd = code.areturn();
158         Instruction handlerStart = code.astore().setLocal(1);
159         code.anew().setType(NoClassDefFoundError.class);
160         code.dup();
161         code.aload().setLocal(1);
162         code.invokevirtual().setMethod(Throwable.class, "getMessage", 
163             String.class, null);
164         code.invokespecial().setMethod(NoClassDefFoundError.class, "<init>", 
165             void.class, _params);
166         code.athrow();
167         code.addExceptionHandler(tryStart, tryEnd, handlerStart,
168             ClassNotFoundException.class);
169         return method;
170     }
171 
172     /***
173      * Return the wrapper type for the given primitive class, or null
174      * if the given name is not a primitive type. The given name should
175      * be in external form.
176      */
177     private static Class getWrapperClass(String name) {
178         if (name == null)
179             return null;
180         return (Class) _wrappers.get(name);
181     }
182 }