001 package serp.bytecode;
002
003 import java.util.*;
004
005 import serp.bytecode.lowlevel.*;
006
007 /**
008 * Pseudo-instruction used to place {@link Class} objects onto the stack.
009 * This logical instruction may actually involve a large chunk of code, and
010 * may even add static synthetic fields and methods to the owning class.
011 * Therefore, once the type of class being loaded is set, it cannot
012 * be changed. Also, this instruction is invalid as the target of
013 * any jump instruction or exception handler.
014 *
015 * @author Abe White
016 */
017 public class ClassConstantInstruction {
018 private static final Class[] _params = new Class[] { String.class };
019 private static final Map _wrappers = new HashMap();
020 static {
021 _wrappers.put(byte.class.getName(), Byte.class);
022 _wrappers.put(boolean.class.getName(), Boolean.class);
023 _wrappers.put(char.class.getName(), Character.class);
024 _wrappers.put(double.class.getName(), Double.class);
025 _wrappers.put(float.class.getName(), Float.class);
026 _wrappers.put(int.class.getName(), Integer.class);
027 _wrappers.put(long.class.getName(), Long.class);
028 _wrappers.put(short.class.getName(), Short.class);
029 }
030
031 private Instruction _ins = null;
032 private Code _code = null;
033 private BCClass _class = null;
034 private boolean _invalid = false;
035
036 ClassConstantInstruction(BCClass bc, Code code, Instruction nop) {
037 _class = bc;
038 _code = code;
039 _ins = nop;
040 }
041
042 /**
043 * Set the type of class being loaded.
044 *
045 * @return the first Instruction of the block added by setting the type
046 * @throws IllegalStateException if type has already been set
047 */
048 public Instruction setClass(String name) {
049 name = _class.getProject().getNameCache().getExternalForm(name, false);
050 setClassName(name, getWrapperClass(name));
051 return _ins;
052 }
053
054 /**
055 * Set the type of class being loaded.
056 *
057 * @return the first Instruction of the block added by setting the type
058 * @throws IllegalStateException if type has already been set
059 */
060 public Instruction setClass(Class type) {
061 return setClass(type.getName());
062 }
063
064 /**
065 * Set the type of class being loaded.
066 *
067 * @return the first Instruction of the block added by setting the type
068 * @throws IllegalStateException if type has already been set
069 */
070 public Instruction setClass(BCClass type) {
071 return setClass(type.getName());
072 }
073
074 /**
075 * Set the name of the class to load.
076 */
077 private void setClassName(String name, Class wrapper) {
078 if (_invalid)
079 throw new IllegalStateException();
080
081 // remember the position of the code iterator
082 Instruction before = (_code.hasNext()) ? _code.next() : null;
083 _code.before(_ins);
084 _code.next();
085 if (wrapper != null)
086 _code.getstatic().setField(wrapper, "TYPE", Class.class);
087 else
088 setObject(name);
089
090 // move to the old position
091 if (before != null)
092 _code.before(before);
093 else
094 _code.afterLast();
095 _invalid = true;
096 }
097
098 /**
099 * 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 }