1 package gnu.kawa.reflect; 2 import gnu.mapping.*; 3 import gnu.expr.*; 4 import gnu.bytecode.*; 5 import gnu.lists.FString; 6 import gnu.kawa.lispexpr.LangPrimType; 7 8 public class SlotGet extends Procedure2 9 implements HasSetter, CanInline, Inlineable 10 { 11 static Class [] noClasses = { }; 12 13 14 boolean isStatic; 15 16 Procedure setter; 17 public static final SlotGet field 18 = new SlotGet("field", false, SlotSet.set$Mnfield$Ex); 19 public static final SlotGet slotRef 20 = new SlotGet("slot-ref", false, SlotSet.set$Mnfield$Ex); 21 public static final SlotGet staticField 22 = new SlotGet("static-field", true, SlotSet.set$Mnstatic$Mnfield$Ex); 23 24 public SlotGet(String name, boolean isStatic) 25 { 26 super(name); 27 this.isStatic = isStatic; 28 } 29 30 public SlotGet(String name, boolean isStatic, Procedure setter) 31 { 32 super(name); 33 this.isStatic = isStatic; 34 this.setter = setter; 35 } 36 37 public static Object field(Object obj, String fname) 38 { 39 return field.apply2(obj, fname); 40 } 41 42 public static Object staticField(Object obj, String fname) 43 { 44 return staticField.apply2(obj, fname); 45 } 46 47 public Object apply2 (Object arg1, Object arg2) 48 { 49 String name, fname; 50 String getName = null, isName = null; 51 if (arg2 instanceof gnu.bytecode.Field) 52 { 53 fname = ((gnu.bytecode.Field) arg2).getName(); 54 name = Compilation.demangleName(fname, true); 55 } 56 else if (arg2 instanceof gnu.bytecode.Method) 57 { 58 String mname = ((gnu.bytecode.Method) arg2).getName(); 59 name = Compilation.demangleName(mname, false); 60 if (mname.startsWith("get")) 61 getName = mname; 62 else if (mname.startsWith("is")) 63 isName = mname; 64 fname = null; 65 } 66 else if (! (arg2 instanceof String ) && ! (arg2 instanceof FString)) 67 throw new WrongType(this, 2, arg2, "string"); 68 else 69 { 70 name = arg2.toString(); 71 fname = gnu.expr.Compilation.mangleNameIfNeeded(name); 72 } 73 if ("class".equals(fname)) 75 fname = "class"; 76 else if ("length".equals(fname)) 77 fname = "length"; 78 return getSlotValue(isStatic, arg1, name, fname, getName, isName, 79 Language.getDefaultLanguage()); 80 } 81 82 87 public static Object 88 getSlotValue (boolean isStatic, Object obj, String name, String fname, 89 String getName, String isName, Language language) 90 { 91 Class clas = isStatic ? coerceToClass(obj) : obj.getClass(); 92 if (fname == "length" && clas.isArray()) 93 { 94 int length = java.lang.reflect.Array.getLength(obj); 95 return language.coerceToObject(length); 96 } 97 if (fname == "class") 98 return clas; 99 boolean illegalAccess = false; 100 if (fname != null) 101 { 102 java.lang.reflect.Field field; 103 try 104 { 105 field = clas.getField(fname); 106 } 107 catch (Exception ex) 108 { 109 field = null; 110 } 111 if (field != null) 112 { 113 if (isStatic 114 && (field.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0) 115 throw new RuntimeException ("cannot access non-static field '" 116 + fname + '\''); 117 try 118 { 119 return language.coerceToObject(field.getType(), field.get(obj)); 120 } 121 catch (IllegalAccessException ex) 122 { 123 illegalAccess = true; 124 } 125 catch (Exception ex) 126 { 127 ex.printStackTrace(); } 129 } 130 } 131 132 try 134 { 135 String mname = null; 136 java.lang.reflect.Method getmethod = null; 137 138 try { 139 mname = getName != null ? getName 140 : ClassExp.slotToMethodName("get", name); 141 getmethod = clas.getMethod(mname, noClasses); 142 } catch (Exception getEx) { 143 mname = isName != null ? isName 144 : ClassExp.slotToMethodName("is", name); 145 getmethod = clas.getMethod(mname, noClasses); 146 } 147 148 if (isStatic 149 && (getmethod.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0) 150 throw new RuntimeException ("cannot call non-static getter method '" 151 + mname + '\''); 152 Object result = getmethod.invoke(obj, Values.noArgs); 153 result = language.coerceToObject(getmethod.getReturnType(), result); 154 return result; 155 } 156 catch (java.lang.reflect.InvocationTargetException ex) 157 { 158 throw WrappedException.wrapIfNeeded(ex.getTargetException()); 159 } 160 catch (IllegalAccessException ex) 161 { 162 illegalAccess = true; 163 } 164 catch (java.lang.NoSuchMethodException ex) 165 { 166 } 167 if (illegalAccess) 168 throw new RuntimeException ("illegal access for field "+fname); 169 else 170 throw new RuntimeException ("no such field "+fname 171 +" in "+clas.getName()); 172 } 173 174 static Class coerceToClass(Object obj) 175 { 176 if (obj instanceof Class ) 177 return (Class ) obj; 178 if (obj instanceof gnu.bytecode.Type) 179 return ((gnu.bytecode.Type) obj).getReflectClass(); 180 throw new RuntimeException ("argument is neither Class nor Type"); 181 } 182 183 public void setN (Object [] args) 184 { 185 int nargs = args.length; 186 if (nargs != 3) 187 throw new WrongArguments(getSetter(), nargs); 188 set2(args[0], args[1], args[2]); 189 } 190 191 public void set2 (Object obj, Object name, Object value) 192 { 193 SlotSet.apply(isStatic, obj, (String ) name, value); 194 } 195 196 200 public static Member 201 lookupMember (ClassType clas, String name, ClassType caller) 202 { 203 gnu.bytecode.Field field 204 = clas.getField(Compilation.mangleNameIfNeeded(name), -1); 205 if (field != null) 206 { 207 if (caller == null) 208 caller = Type.pointer_type; 209 if (caller.isAccessible(field.getDeclaringClass(), 210 field.getModifiers())) 211 return field; 212 } 213 214 String getname = ClassExp.slotToMethodName("get", name); 216 gnu.bytecode.Method method = clas.getMethod(getname, Type.typeArray0); 217 if (method == null) 218 return field; 219 else 220 return method; 221 } 222 223 public Expression inline (ApplyExp exp, ExpWalker walker) 224 { 225 Compilation comp = walker.getCompilation(); 226 Language language = comp.getLanguage(); 227 Type type; 228 Expression[] args = exp.getArgs(); 229 Expression arg0 = args[0]; 230 Expression arg1 = args[1]; 231 String name = null; 232 if (arg1 instanceof QuoteExp) 233 { 234 Object val1 = ((QuoteExp) arg1).getValue(); 235 if (val1 instanceof String 236 || val1 instanceof FString 237 || val1 instanceof Symbol) 238 name = val1.toString(); 239 } 240 if (isStatic) 241 { 242 type = language.getTypeFor(arg0); 243 int known = Invoke.checkKnownClass(type, comp); 244 if (known < 0) 245 return exp; 246 if ("class".equals(name)) 247 { 248 if (known > 0) 249 return QuoteExp.getInstance(type.getReflectClass()); 250 Method method 251 = Compilation.typeType.getDeclaredMethod("getReflectClass", 0); 252 return new ApplyExp(method, new Expression[] { arg0 }); 253 } 254 if (type != null) 255 { 256 Expression[] nargs 257 = new Expression[] { new QuoteExp(type), arg1 }; 258 ApplyExp nexp = new ApplyExp(exp.getFunction(), nargs); 259 nexp.setLine(exp); 260 exp = nexp; 261 } 262 } 263 else 264 type = arg0.getType(); 265 if (type instanceof ClassType && name != null) 266 { 267 ClassType ctype = (ClassType) type; 268 ClassType caller = comp.curClass != null ? comp.curClass 269 : comp.mainClass; 270 Member part = lookupMember(ctype, name, caller); 271 if (part instanceof gnu.bytecode.Field) 272 { 273 gnu.bytecode.Field field = (gnu.bytecode.Field) part; 274 ctype = field.getDeclaringClass(); 275 int modifiers = field.getModifiers(); 276 boolean isStaticField = (modifiers & Access.STATIC) != 0; 277 if (isStatic && ! isStaticField) 278 return new ErrorExp("cannot access non-static field `" + name 279 + "' using `" + getName() + '\'', comp); 280 if (caller != null && ! caller.isAccessible(ctype, modifiers)) 281 return new ErrorExp("field "+ctype.getName()+'.'+name 282 +" is not accessible here", comp); 283 } 284 285 else if (part instanceof gnu.bytecode.Method) 286 { 287 gnu.bytecode.Method method = (gnu.bytecode.Method) part; 288 ctype = method.getDeclaringClass(); 289 int modifiers = method.getModifiers(); 290 boolean isStaticMethod = method.getStaticFlag(); 291 if (isStatic && ! isStaticMethod) 292 return new ErrorExp("cannot call non-static getter method `" 293 + name + "' using `" + getName() + '\'', comp); 294 if (caller != null && ! caller.isAccessible(ctype, modifiers)) 295 return new ErrorExp( "method "+method +" is not accessible here", 296 comp); 297 } 298 if (part != null) 299 { 300 Expression[] nargs 301 = new Expression[] { arg0, new QuoteExp(part) }; 302 ApplyExp nexp = new ApplyExp(exp.getFunction(), nargs); 303 nexp.setLine(exp); 304 return nexp; 305 } 306 if (type != Type.pointer_type) 307 comp.error('e', "no slot `"+name+"' in "+ctype.getName()); 308 } 309 if (name != null && ! (type instanceof ArrayType)) 310 { 311 String fname = gnu.expr.Compilation.mangleNameIfNeeded(name); 312 fname = fname.intern(); 315 String getName = ClassExp.slotToMethodName("get", name); 316 String isName = ClassExp.slotToMethodName("is", name); 317 ApplyExp nexp 318 = new ApplyExp(Invoke.invokeStatic, 319 new Expression[] { 320 QuoteExp.getInstance("gnu.kawa.reflect.SlotGet"), 321 QuoteExp.getInstance("getSlotValue"), 322 isStatic ? QuoteExp.trueExp : QuoteExp.falseExp, 323 args[0], 324 QuoteExp.getInstance(name), 325 QuoteExp.getInstance(fname), 326 QuoteExp.getInstance(getName), 327 QuoteExp.getInstance(isName), 328 QuoteExp.getInstance(language)}); 329 nexp.setLine(exp); 330 return ((InlineCalls) walker).walkApplyOnly(nexp); 331 } 332 return exp; 333 } 334 335 public void compile (ApplyExp exp, Compilation comp, Target target) 336 { 337 Expression[] args = exp.getArgs(); 338 Expression arg0 = args[0]; 339 Expression arg1 = args[1]; 340 Language language = comp.getLanguage(); 341 Type type = isStatic ? language.getTypeFor(arg0) 342 : arg0.getType(); 343 CodeAttr code = comp.getCode(); 344 if (type instanceof ClassType && arg1 instanceof QuoteExp) 345 { 346 ClassType ctype = (ClassType) type; 347 Object part = ((QuoteExp) arg1).getValue(); 348 if (part instanceof gnu.bytecode.Field) 349 { 350 gnu.bytecode.Field field = (gnu.bytecode.Field) part; 351 int modifiers = field.getModifiers(); 352 boolean isStaticField = (modifiers & Access.STATIC) != 0; 353 args[0].compile(comp, 354 isStaticField ? Target.Ignore 355 : Target.pushValue(ctype)); 356 if (isStaticField) 357 { 358 boolean inlined = false; 359 388 if (! inlined) 389 code.emitGetStatic(field); 390 } 391 else 392 code.emitGetField(field); 393 Type ftype = field.getType(); 394 Class fclass = ftype.getReflectClass(); 395 if (fclass != null) 396 ftype = language.getTypeFor(fclass); 397 target.compileFromStack(comp, ftype); 398 return; 399 } 400 if (part instanceof Method) 401 { 402 gnu.bytecode.Method method = (gnu.bytecode.Method) part; 403 int modifiers = method.getModifiers(); 404 boolean isStaticMethod = method.getStaticFlag(); 405 args[0].compile(comp, 406 isStaticMethod ? Target.Ignore 407 : Target.pushValue(ctype)); 408 if (isStaticMethod) 409 code.emitInvokeStatic(method); 410 else if (ctype.isInterface()) 411 code.emitInvokeInterface(method); 412 else 413 code.emitInvokeVirtual(method); 414 target.compileFromStack(comp, method.getReturnType()); 415 return; 416 } 417 } 418 String name = ClassMethods.checkName(arg1); 419 if (type instanceof ArrayType && "length".equals(name) && ! isStatic) 420 { 421 args[0].compile(comp, Target.pushValue(type)); 422 code.emitArrayLength(); 423 target.compileFromStack(comp, LangPrimType.intType); return; 425 } 426 ApplyExp.compile(exp, comp, target); 427 } 428 429 public Type getReturnType (Expression[] args) 430 { 431 int nargs = args.length; 432 if (nargs == 2) 433 { 434 Expression arg0 = args[0]; 435 Expression arg1 = args[1]; 436 if (arg1 instanceof QuoteExp) 437 { 438 Object part = ((QuoteExp) arg1).getValue(); 439 if (part instanceof gnu.bytecode.Field) 440 return ((gnu.bytecode.Field) part).getType(); 441 if (part instanceof gnu.bytecode.Method) 442 return ((gnu.bytecode.Method) part).getReturnType(); 443 if (! isStatic && arg0.getType() instanceof ArrayType 444 && "length".equals(ClassMethods.checkName(arg1, true))) 445 return gnu.kawa.lispexpr.LangPrimType.intType; } 447 } 448 return Type.pointer_type; 449 } 450 451 public Procedure getSetter() 452 { 453 return setter == null ? super.getSetter() : setter; 454 } 455 456 462 public static ApplyExp makeGetField(Expression value, String fieldName) 463 { 464 Expression[] args = new Expression[2]; 465 args[0] = value; 466 args[1] = new QuoteExp(fieldName); 467 return new ApplyExp(gnu.kawa.reflect.SlotGet.field, args); 468 } 469 } 470 | Popular Tags |