1 package gnu.kawa.reflect; 2 import gnu.mapping.*; 3 import gnu.expr.*; 4 import gnu.bytecode.*; 5 import gnu.lists.FString; 6 import java.lang.reflect.Array ; 7 import gnu.kawa.lispexpr.ClassNamespace; 9 public class Invoke extends ProcedureN implements CanInline 10 { 11 23 char kind; 24 25 Language language; 26 27 public static final Invoke invoke = new Invoke("invoke", '*'); 28 public static final Invoke invokeStatic = new Invoke("invoke-static", 'S'); 29 public static final Invoke invokeSpecial = new Invoke("invoke-special", 'P'); 30 public static final Invoke make = new Invoke("make", 'N'); 31 32 public Invoke(String name, char kind) 33 { 34 super(name); 35 this.kind = kind; 36 this.language = Language.getDefaultLanguage(); 37 } 38 39 public Invoke(String name, char kind, Language language) 40 { 41 super(name); 42 this.kind = kind; 43 this.language = language; 44 } 45 46 public static Object invoke$V(Object [] args) throws Throwable 47 { 48 return invoke.applyN(args); 49 } 50 51 public static Object invokeStatic$V(Object [] args) throws Throwable 52 { 53 return invokeStatic.applyN(args); 54 } 55 56 public static Object make$V(Object [] args) throws Throwable 57 { 58 return make.applyN(args); 59 } 60 61 private static ObjectType typeFrom (Object arg, Invoke thisProc) 62 { 63 if (arg instanceof Class ) 64 arg = Type.make((Class ) arg); 65 if (arg instanceof ObjectType) 66 return (ObjectType) arg; 67 if (arg instanceof String || arg instanceof FString) 68 return ClassType.make(arg.toString()); 69 if (arg instanceof Symbol) 70 return ClassType.make(((Symbol) arg).getName()); 71 if (arg instanceof ClassNamespace) 72 return ((ClassNamespace) arg).getClassType(); 73 throw new WrongType(thisProc, 0, arg, "class-specifier"); 74 } 75 76 public void apply (CallContext ctx) throws Throwable 77 { 78 Object [] args = ctx.getArgs(); 79 if (kind=='S' || kind=='V' || kind=='s' || kind=='*') 80 { 81 int nargs = args.length; 84 Procedure.checkArgCount(this, nargs); 85 Object arg0 = args[0]; 86 ClassType dtype = (ClassType) 87 ((kind == 'S' || kind == 's') ? typeFrom(arg0, this) 88 : Type.make(arg0.getClass())); 89 Procedure proc = lookupMethods(dtype, args[1]); 90 Object [] margs = new Object [nargs-(kind == 'S' ? 2 : 1)]; 91 int i = 0; 92 if (kind == 'V' || kind == '*') 93 margs[i++] = args[0]; 94 System.arraycopy(args, 2, margs, i, nargs - 2); 95 proc.checkN(margs, ctx); 96 } 97 else 98 ctx.writeValue(this.applyN(args)); 99 } 100 101 public Object applyN (Object [] args) throws Throwable 102 { 103 if (kind == 'P') 104 throw new RuntimeException (getName() 105 + ": invoke-special not allowed at run time"); 106 107 int nargs = args.length; 108 Procedure.checkArgCount(this, nargs); 109 Object arg0 = args[0]; 110 ObjectType dtype = (kind != 'V' && kind != '*' ? typeFrom(arg0, this) 111 : (ObjectType) Type.make(arg0.getClass())); 112 Object mname; 113 if (kind == 'N') 114 { 115 mname = null; 116 if (dtype instanceof TypeValue) 117 { 118 Procedure constructor = ((TypeValue) dtype).getConstructor(); 119 if (constructor != null) 120 { 121 nargs--; 122 Object [] xargs = new Object [nargs]; 123 System.arraycopy(args, 1, xargs, 0, nargs); 124 return constructor.applyN(xargs); 125 } 126 } 127 if (dtype instanceof PairClassType) 128 { 129 PairClassType ptype = (PairClassType) dtype; 130 dtype = ptype.instanceType; 131 } 132 if (dtype instanceof ArrayType) 133 { 134 Type elementType = ((ArrayType) dtype).getComponentType(); 135 int len; 136 len = args.length-1; 137 String name; 138 int length; 139 int i; 140 boolean lengthSpecified; 141 if (len >= 2 && args[1] instanceof Keyword 142 && ("length".equals(name = ((Keyword) args[1]).getName()) 143 || "size".equals(name))) 144 { 145 length = ((Number ) args[2]).intValue(); 146 i = 3; 147 lengthSpecified = true; 148 } 149 else 150 { 151 length = len; 152 i = 1; 153 lengthSpecified = false; 154 } 155 Object arr = Array.newInstance(elementType.getReflectClass(), 156 length); 157 int index = 0; 158 for (; i <= len; i++) 159 { 160 Object arg = args[i]; 161 if (lengthSpecified && arg instanceof Keyword && i < len) 162 { 163 String kname = ((Keyword) arg).getName(); 164 try 165 { 166 index = Integer.parseInt(kname); 167 } 168 catch (Throwable ex) 169 { 170 throw new RuntimeException ("non-integer keyword '"+kname+"' in array constructor"); 171 } 172 arg = args[++i]; 173 } 174 Array.set(arr, index, elementType.coerceFromObject(arg)); 175 index++; 176 } 177 return arr; 178 } 179 } 180 else 181 { 182 mname = args[1]; 183 } 184 MethodProc proc = lookupMethods((ClassType) dtype, mname); 185 if (kind != 'N') 186 { 187 Object [] margs = new Object [nargs-(kind == 'S' || kind == 's' ? 2 : 1)]; 188 int i = 0; 189 if (kind == 'V' || kind == '*') 190 margs[i++] = args[0]; 191 System.arraycopy(args, 2, margs, i, nargs - 2); 192 return proc.applyN(margs); 193 } 194 else 195 { 196 CallContext vars = CallContext.getInstance(); 197 int err = proc.matchN(args, vars); 198 if (err == 0) 199 return vars.runUntilValue(); 200 else if ((nargs & 1) == 1) 201 { 202 for (int i = 1; i < nargs; i += 2) 204 { 205 if (! (args[i] instanceof Keyword)) 206 throw MethodProc.matchFailAsException(err, proc, args); 207 } 208 209 Object result; 210 result = proc.apply1(args[0]); 211 for (int i = 1; i < nargs; i += 2) 212 { 213 Keyword key = (Keyword) args[i]; 214 Object arg = args[i+1]; 215 SlotSet.apply(false, result, key.getName(), arg); 216 } 217 return result; 218 } 219 throw MethodProc.matchFailAsException(err, proc, args); 220 } 221 } 222 223 public int numArgs() 224 { 225 return (-1 << 12) | (kind == 'N' ? 1 : 2); 226 } 227 228 private PrimProcedure[] cacheMethods; 229 private Expression[] cacheArgs; 230 private int cacheDefinitelyApplicableMethodCount; 231 private int cachePossiblyApplicableMethodCount; 232 233 protected MethodProc lookupMethods(ClassType dtype, Object name) 234 { 235 String mname; 236 if (kind == 'N') 237 mname = "<init>"; 238 else 239 { 240 if (name instanceof String || name instanceof FString) 241 mname = name.toString(); 242 else if (name instanceof Symbol) 243 mname = ((Symbol) name).getName(); 244 else 245 throw new WrongType(this, 1, null); 246 mname = Compilation.mangleName(mname); 247 } 248 MethodProc proc = ClassMethods.apply(dtype, mname, 249 kind == 'P' ? 'P' 250 : kind == '*' || kind == 'V' ? 'V' 251 : '\0', 252 language); 253 if (proc == null) 254 throw new RuntimeException (getName() + ": no method named `" 255 + mname + "' in class " + dtype.getName()); 256 return proc; 257 } 258 259 protected PrimProcedure[] getMethods(ClassType ctype, String mname, 260 Expression[] args, int margsLength, 261 int argsStartIndex, int objIndex, 262 ClassType caller) 263 { 264 if (args == cacheArgs) 265 return cacheMethods; 266 267 Type[] atypes = new Type[margsLength]; 268 269 int dst = 0; 270 if (objIndex >= 0) 271 atypes[dst++] = ctype; 272 for (int src = argsStartIndex; 273 src < args.length && dst < atypes.length; 274 src++, dst++) 275 atypes[dst] = args[src].getType(); 276 277 PrimProcedure[] methods 278 = ClassMethods.getMethods(ctype, mname, 279 kind == 'P' ? 'P' 280 : kind == '*' || kind == 'V' ? 'V' 281 : '\0', 282 caller, language); 283 284 long num = ClassMethods.selectApplicable(methods, atypes); 285 cacheArgs = args; 286 cacheDefinitelyApplicableMethodCount = (int) (num >> 32); 287 cachePossiblyApplicableMethodCount = (int) num; 288 cacheMethods = methods; 289 return cacheMethods; 290 } 291 292 294 static Object [] checkKeywords(Type type, Expression[] args, 295 int start, ClassType caller) 296 { 297 int len = args.length; 298 if (((len - start) & 1) != 0) 299 return null; 300 Object [] fields = new Object [(len-start) >> 1]; 301 for (int i = fields.length; -- i>= 0; ) 302 { 303 Expression arg = args[start + 2 * i]; 304 if (! (arg instanceof QuoteExp)) 305 return null; 306 Object value = ((QuoteExp) arg).getValue(); 307 if (! (value instanceof Keyword)) 308 return null; 309 String name = ((Keyword) value).getName(); 310 Member slot = SlotSet.lookupMember((ClassType) type, name, caller); 311 fields[i] = slot != null ? (Object ) slot : (Object ) name; 312 } 313 return fields; 314 } 315 316 321 public static int checkKnownClass (Type type, Compilation comp) 322 { 323 if (type instanceof ClassType && ((ClassType) type).isExisting()) 324 { 325 try 326 { 327 type.getReflectClass(); 328 return 1; 329 } 330 catch (Exception ex) 331 { 332 comp.error('e', "unknown class: " + type.getName()); 333 return -1; 334 } 335 } 336 return 0; 337 } 338 339 342 343 public static ApplyExp inlineClassName (ApplyExp exp, int carg, 344 InlineCalls walker) 345 { 346 Compilation comp = walker.getCompilation(); 347 Language language = comp.getLanguage(); 348 Expression[] args = exp.getArgs(); 349 if (args.length > carg) 350 { 351 Type type = language.getTypeFor(args[carg]); 352 if (! (type instanceof Type)) 353 return exp; 354 checkKnownClass(type, comp); 355 Expression[] nargs = new Expression[args.length]; 356 System.arraycopy(args, 0, nargs, 0, args.length); 357 nargs[carg] = new QuoteExp(type); 358 ApplyExp nexp = new ApplyExp(exp.getFunction(), nargs); 359 nexp.setLine(exp); 360 return nexp; 361 } 362 return exp; 363 } 364 365 public Expression inline (ApplyExp exp, ExpWalker walker) 366 { 367 Compilation comp = walker.getCompilation(); 368 Expression[] args = exp.getArgs(); 369 int nargs = args.length; 370 if (! comp.mustCompile 371 || nargs == 0 || ((kind == 'V' || kind == '*') && nargs == 1)) 374 return exp; 375 ObjectType type; 376 Expression arg0 = args[0]; 377 Type type0 = (kind == 'V' || kind == '*' ? arg0.getType() : language.getTypeFor(arg0)); 378 if (type0 instanceof PairClassType) 379 type = ((PairClassType) type0).instanceType; 380 else if (type0 instanceof ObjectType) 381 type = (ObjectType) type0; 382 else 383 type = null; 384 String name = getMethodName(args); 385 386 int margsLength, argsStartIndex, objIndex; 387 if (kind == 'V' || kind == '*') { 389 margsLength = nargs - 1; 390 argsStartIndex = 2; 391 objIndex = 0; 392 } 393 else if (kind == 'N') { 395 margsLength = nargs; 396 argsStartIndex = 0; 397 objIndex = -1; 398 } 399 else if (kind == 'S' || kind == 's') { 401 margsLength = nargs - 2; 402 argsStartIndex = 2; 403 objIndex = -1; 404 } 405 else if (kind == 'P') { 407 margsLength = nargs - 2; 408 argsStartIndex = 3; 409 objIndex = 1; 410 } 411 else 412 return exp; 413 414 if (kind == 'N' && type instanceof ArrayType) 415 { 416 ArrayType atype = (ArrayType) type; 417 Type elementType = atype.getComponentType(); 418 Expression sizeArg = null; 419 boolean lengthSpecified = false; 420 if (args.length >= 3 && args[1] instanceof QuoteExp) 421 { 422 Object arg1 = ((QuoteExp) args[1]).getValue(); 423 if (arg1 instanceof Keyword 424 && ("length".equals(name = ((Keyword) arg1).getName()) 425 || "size".equals(name))) 426 { 427 sizeArg = args[2]; 428 lengthSpecified = true; 429 } 430 } 431 if (sizeArg == null) 432 sizeArg = QuoteExp.getInstance(new Integer (args.length-1)); 433 Expression alloc = new ApplyExp(new ArrayNew(elementType), 434 new Expression[] { sizeArg } ); 435 if (lengthSpecified && args.length == 3) 436 return alloc; 437 LetExp let = new LetExp(new Expression[] { alloc }); 438 Declaration adecl = let.addDeclaration((String ) null, atype); 439 adecl.noteValue(alloc); 440 BeginExp begin = new BeginExp(); 441 int index = 0; 442 for (int i = lengthSpecified ? 3 : 1; i < args.length; i++) 443 { 444 Expression arg = args[i]; 445 if (lengthSpecified && i+1 < args.length && arg instanceof QuoteExp) 446 { 447 Object key = ((QuoteExp) arg).getValue(); 448 if (key instanceof Keyword) 449 { 450 String kname = ((Keyword) key).getName(); 451 try 452 { 453 index = Integer.parseInt(kname); 454 arg = args[++i]; 455 } 456 catch (Throwable ex) 457 { 458 comp.error('e', "non-integer keyword '"+kname+"' in array constructor"); 459 return exp; 460 } 461 } 462 } 463 begin.add(new ApplyExp(new ArraySet(elementType), 464 new Expression[] { 465 new ReferenceExp(adecl), 466 QuoteExp.getInstance(new Integer (index)), 467 arg})); 468 index++; 469 } 470 begin.add(new ReferenceExp(adecl)); 471 let.body = begin; 472 return let; 473 } 474 else if (type != null && name != null) 475 { 476 if (type instanceof TypeValue && kind == 'N') 477 { 478 Procedure constructor = ((TypeValue) type).getConstructor(); 479 if (constructor != null) 480 { 481 Expression[] xargs = new Expression[nargs]; 482 System.arraycopy(args, 1, xargs, 0, nargs-1); 483 return ((InlineCalls) walker) 484 .walkApplyOnly(new ApplyExp(constructor, xargs)); 485 } 486 } 487 PrimProcedure[] methods; 488 int okCount, maybeCount; 489 ClassType caller = comp == null ? null 490 : comp.curClass != null ? comp.curClass 491 : comp.mainClass; 492 synchronized (this) 493 { 494 try 495 { 496 methods = getMethods((ClassType) type, name, args, 497 margsLength, argsStartIndex, objIndex, 498 caller); 499 } 500 catch (Exception ex) 501 { 502 comp.error('w', "unknown class: " + type.getName()); 503 methods = null; 504 } 505 okCount = cacheDefinitelyApplicableMethodCount; 506 maybeCount = cachePossiblyApplicableMethodCount; 507 } 508 if (methods != null) 509 { 510 int index = -1; 511 if (methods.length == 0) 512 { 513 if (comp.getBooleanOption("warn-invoke-unknown-method", true)) 514 comp.error('w', "no accessible method '"+name+"' in "+type.getName()); 515 } 516 else if (okCount + maybeCount == 0) 517 { 518 Object [] slots; 519 if (kind == 'N' 520 && (ClassMethods.selectApplicable(methods, 521 new Type[] { Compilation.typeClassType }) 522 >> 32) == 1 523 && (slots = checkKeywords(type, args, 1, caller)) != null) 524 { 525 StringBuffer errbuf = null; 526 for (int i = 0; i < slots.length; i++) 527 { 528 if (slots[i] instanceof String ) 529 { 530 if (errbuf == null) 531 { 532 errbuf = new StringBuffer (); 533 errbuf.append("no field or setter "); 534 } 535 else 536 errbuf.append(", "); 537 errbuf.append('`'); 538 errbuf.append(slots[i]); 539 errbuf.append('\''); 540 } 541 } 542 if (errbuf != null) 543 { 544 errbuf.append(" in class "); 545 errbuf.append(type.getName()); 546 comp.error('w', errbuf.toString()); 547 } 548 else 549 { 550 ApplyExp e = new ApplyExp(methods[0], 551 new Expression[] { arg0 }); 552 for (int i = 0; i < slots.length; i++) 553 { 554 Expression[] sargs 555 = { e, new QuoteExp(slots[i]), args[2 * i + 2] }; 556 e = new ApplyExp(SlotSet.setFieldReturnObject, 557 sargs); 558 } 559 return e.setLine(exp); 560 } 561 } 562 else 563 comp.error('w', "no possibly applicable method '" 564 +name+"' in "+type.getName()); 565 } 566 else if (okCount == 1 || (okCount == 0 && maybeCount == 1)) 567 index = 0; 568 else if (okCount > 0) 569 { 570 index = MethodProc.mostSpecific(methods, okCount); 571 if (index < 0) 572 { 573 if (kind == 'S') 574 { 575 for (int i = 0; i < okCount; i++) 579 { 580 if (methods[i].getStaticFlag()) 581 { 582 if (index >= 0) 583 { 584 index = -1; 585 break; 586 } 587 else 588 index = i; 589 } 590 } 591 } 592 } 593 if (index < 0 594 && comp.getBooleanOption("warn-invoke-unknown-method", 595 true)) 596 { 597 StringBuffer sbuf = new StringBuffer (); 598 sbuf.append("more than one definitely applicable method `"); 599 sbuf.append(name); 600 sbuf.append("' in "); 601 sbuf.append(type.getName()); 602 append(methods, okCount, sbuf); 603 comp.error('w', sbuf.toString()); 604 } 605 } 606 else if (comp.getBooleanOption("warn-invoke-unknown-method", true)) 607 { 608 StringBuffer sbuf = new StringBuffer (); 609 sbuf.append("more than one possibly applicable method '"); 610 sbuf.append(name); 611 sbuf.append("' in "); 612 sbuf.append(type.getName()); 613 append(methods, maybeCount, sbuf); 614 comp.error('w', sbuf.toString()); 615 } 616 if (index >= 0) 617 { 618 Expression[] margs = new Expression[margsLength]; 619 int dst = 0; 620 if (objIndex >= 0) 621 margs[dst++] = args[objIndex]; 622 for (int src = argsStartIndex; 623 src < args.length && dst < margs.length; 624 src++, dst++) 625 margs[dst] = args[src]; 626 return new ApplyExp(methods[index], margs).setLine(exp); 627 } 628 } 629 } 630 return exp; 631 } 632 633 private void append (PrimProcedure[] methods, int mcount, StringBuffer sbuf) 634 { 635 for (int i = 0; i < mcount; i++) 636 { 637 sbuf.append("\n candidate: "); 638 sbuf.append(methods[i]); 639 } 640 } 641 642 private String getMethodName(Expression[] args) 643 { 644 if (kind == 'N') 645 return "<init>"; 646 int nameIndex = (kind == 'P' ? 2 : 1); 647 if (args.length >= nameIndex + 1) 648 return ClassMethods.checkName(args[nameIndex], false); 649 return null; 650 } 651 652 658 public static synchronized 659 ApplyExp makeInvokeStatic(ClassType type, String name, Expression[] args) 660 { 661 PrimProcedure method = getStaticMethod(type, name, args); 662 if (method == null) 663 throw new RuntimeException ("missing or ambiguous method `" + name 664 + "' in " + type.getName()); 665 return new ApplyExp(method, args); 666 } 667 668 public static synchronized PrimProcedure 669 getStaticMethod(ClassType type, String name, Expression[] args) 670 { 671 PrimProcedure[] methods = invokeStatic.getMethods(type, name, args, 672 args.length, 0, -1, null); 673 int okCount = invokeStatic.cacheDefinitelyApplicableMethodCount; 674 int maybeCount = invokeStatic.cachePossiblyApplicableMethodCount; 675 int index; 676 if (methods == null) 677 index = -1; 678 else if (okCount > 0) 679 index = MethodProc.mostSpecific(methods, okCount); 680 else if (maybeCount == 1) 681 index = 0; 682 else 683 index = -1; 684 return index < 0 ? null : methods[index]; 685 } 686 } 687 | Popular Tags |