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 }