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
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
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
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
144 method = _class.declareMethod("class$", Class.class, _params);
145 method.setStatic(true);
146 method.makePackage();
147 method.setSynthetic(true);
148
149
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 }