1 // Copyright (c) 1999, 2000, 2008, 2015 Per M.A. Bothner. 2 // This is free software; for terms and warranty disclaimer see ./COPYING. 3 4 package gnu.expr; 5 6 /* #ifdef use:java.lang.invoke */ 7 import java.lang.invoke.*; 8 /* #else */ 9 // import gnu.mapping.CallContext.MethodHandle; 10 /* #endif */ 11 import gnu.bytecode.*; 12 import gnu.mapping.*; 13 import gnu.kawa.lispexpr.LangObjType; 14 import gnu.kawa.reflect.CompileArrays; 15 import gnu.lists.ConsumerWriter; 16 import kawa.SourceMethodType; 17 import java.io.Writer; 18 import java.lang.reflect.Array; 19 import gnu.kawa.functions.MakeSplice; 20 21 /** A primitive Procedure implemented by a plain Java method. */ 22 23 public class PrimProcedure extends MethodProc { 24 private Type retType; 25 26 /** The types of the method parameters. 27 * If known, the types have been coerced to Language-specific parameters. 28 * Does not include the implicit static link argument of some constructors. 29 */ 30 private Type[] argTypes; 31 32 private Method method; 33 34 /** Actual method to invoke. Normally same as method. 35 * However, may have declaring class of the actual caller. 36 * This is similar to what javac does for improved binary compatibility. */ 37 private Method methodForInvoke; 38 39 private int op_code; 40 /** 'P' means use invokespecial; 41 * 'V' means expect a target (this) argument, even if method is static; 42 * '\0' means don't expect a target. */ 43 private char mode; 44 private boolean sideEffectFree; 45 46 /** Do we needs to zero-truncate the returned result? 47 * A Kawa-compiled function will do so before loading the value 48 * on the stack (and hence before returning), but post-return 49 * truncation may be needed if calling a Java method. 50 */ 51 private boolean doFixUnsigned; 52 53 /** If non-null, the LambdaExp that this PrimProcedure implements. */ 54 private LambdaExp source; 55 56 private java.lang.reflect.Member member; 57 opcode()58 public final int opcode() { return op_code; } 59 getReturnType()60 public Type getReturnType () { return retType; } setReturnType(Type retType)61 public void setReturnType (Type retType) { 62 this.doFixUnsigned = true; 63 this.retType = retType; 64 } 65 isSpecial()66 public boolean isSpecial() { return mode == 'P'; } 67 getReturnType(Expression[] args)68 public Type getReturnType (Expression[] args) { return retType; } 69 getDeclaringClass()70 public ClassType getDeclaringClass() { 71 return methodForInvoke == null ? null 72 : methodForInvoke.getDeclaringClass(); 73 } 74 getMethod()75 public Method getMethod () { return method; } 76 setMethodForInvoke(Method m)77 public void setMethodForInvoke(Method m) { 78 methodForInvoke = m; 79 setOpcode(m); 80 } 81 isSideEffectFree()82 public boolean isSideEffectFree () 83 { 84 return sideEffectFree; 85 } 86 setSideEffectFree()87 public void setSideEffectFree () 88 { 89 sideEffectFree = true; 90 } 91 92 /** Return true iff the last parameter is a "rest" argument. */ takesVarArgs()93 public boolean takesVarArgs() { 94 return takesVarArgs(method); 95 } 96 takesVarArgs(Method method)97 public static boolean takesVarArgs(Method method) { 98 if (method != null) { 99 if ((method.getModifiers() & Access.VARARGS) != 0) 100 return true; 101 String name = method.getName(); 102 return name.endsWith("$V") || name.endsWith("$V$X"); 103 } 104 return false; 105 } 106 takesContext()107 public boolean takesContext() 108 { 109 return method != null && takesContext(method); 110 } 111 takesContext(Method method)112 public static boolean takesContext(Method method) 113 { 114 return method.getName().endsWith("$X"); 115 } 116 117 /** Support passing an explicit array to a varargs function. 118 * This is a kludge inherited from Java to support backwards 119 * compatibility after various methods were converte to take varargs. 120 * If Java5-style VARARGS we allow both a variable-length argument list, 121 * or if the last argument already is an array we can use it as is. 122 * The tricky part is we sometimes have to distinguish these cases 123 * at run-time - see the logic for createVarargsArrayIfNeeded in 124 * compileArgs. 125 * FIXME This is needless and unreliable complexity. We should by default 126 * create a varargs array - even if the actual argument is an array. 127 * People should now use splices instead. 128 */ 129 public static boolean explicitArrayAsVarArgsAllowed = false; 130 isApplicable(Type[] argTypes, Type restType)131 public int isApplicable(Type[] argTypes, Type restType) { 132 int app = super.isApplicable(argTypes, restType); 133 int nargs = argTypes.length; 134 if (explicitArrayAsVarArgsAllowed 135 && app == -1 && method != null && restType == null 136 && (method.getModifiers() & Access.VARARGS) != 0 137 && nargs > 0 && argTypes[nargs-1] instanceof ArrayType) 138 { 139 // For a Java5-style VARARGS method, you're also allowed to 140 // explicitly pass in an array as the last argument. 141 Type[] tmp = new Type[nargs]; 142 System.arraycopy(argTypes, 0, tmp, 0, nargs-1); 143 tmp[nargs-1] = ((ArrayType) argTypes[nargs-1]).getComponentType(); 144 return super.isApplicable(tmp, null); 145 } 146 return app; 147 } 148 isAbstract()149 public boolean isAbstract() { 150 return method != null 151 && (method.getModifiers() & Access.ABSTRACT) != 0; 152 } 153 isConstructor()154 public final boolean isConstructor() 155 { 156 // invokespecial == primitive-constructor 157 return opcode() == 183 && mode != 'P'; 158 } 159 160 /** Whether we are passed an argument for the 'target' / 'receiver' / 'this'. 161 * Normally this is false for static methods and true for non-static 162 * methods. However, we may need to be able to call a static method using 163 * {@code object.name(args...)} (Java syntax) or 164 * {@code (invoke object 'name args...)} (Scheme syntax). 165 * This includes when the {@code object} is implied. 166 * In this case we need to ignore the first argument's value. 167 */ takesTarget()168 public boolean takesTarget () 169 { 170 return mode != '\0'; 171 } 172 173 /** The (minimum, number) of arguments. 174 * Doesn't not count implicit CallContext argument. 175 * Does count 'this' argument for non-static methods. 176 * Does count an implicit staticLink argument for constructor. 177 */ numArgs()178 public int numArgs() 179 { 180 int num = argTypes.length; 181 if (takesTarget()) 182 num++; 183 if (takesContext()) 184 num--; 185 return takesVarArgs() ? (num - 1) + (-1 << 12) : num + (num << 12); 186 } 187 applyToConsumer(Procedure proc, CallContext ctx)188 public static Object applyToConsumer(Procedure proc, CallContext ctx) throws Throwable { 189 return ((PrimProcedure) proc).applyToConsumerX(ctx); 190 } applyToConsumerX(CallContext ctx)191 public Object applyToConsumerX(CallContext ctx) throws Throwable { 192 int nargs = ctx.getArgCount(); 193 boolean takesVarArgs = takesVarArgs(); 194 int fixArgs = minArgs(); 195 if (nargs < fixArgs) { 196 ctx.matchError(NO_MATCH_TOO_FEW_ARGS|fixArgs); 197 return ctx; 198 } 199 if (! takesVarArgs && nargs > fixArgs) { 200 ctx.matchError(NO_MATCH_TOO_MANY_ARGS|fixArgs); 201 return ctx; 202 } 203 int paramCount = argTypes.length; 204 Object restArray = null; 205 int extraCount = (takesTarget() || isConstructor()) ? 1 : 0; 206 boolean takesContext = takesContext(); 207 Object[] rargs = new Object[paramCount]; 208 if (takesContext) 209 rargs[--paramCount] = ctx; 210 Object extraArg; 211 Type elementType = null; 212 if (takesVarArgs) { 213 Type restType = argTypes[paramCount-1]; 214 if (restType == Compilation.scmListType 215 || restType == LangObjType.listType 216 || restType == LangObjType.argListType 217 || restType == LangObjType.argVectorType) { 218 nargs = fixArgs+1; 219 elementType = Type.objectType; 220 } else { 221 ArrayType restArrayType = (ArrayType) restType; 222 elementType = restArrayType.getComponentType(); 223 Class elementClass = elementType.getReflectClass(); 224 restArray = Array.newInstance(elementClass, nargs-fixArgs); 225 rargs[paramCount-1] = restArray; 226 } 227 } 228 if (isConstructor()) 229 extraArg = ctx.getNextArg(); 230 else if (extraCount != 0) { 231 try { 232 extraArg = getDeclaringClass().coerceFromObject(ctx.getNextArg()); 233 } catch (ClassCastException ex) { 234 ctx.matchError(NO_MATCH_BAD_TYPE|1); 235 return ctx; 236 } 237 } else 238 extraArg = null; 239 for (int i = extraCount; i < nargs; i++) { 240 // Why is extraArg used twice if isConstructor() 241 Object arg = i==0&&isConstructor() ? extraArg 242 : i==fixArgs && restArray == null ? ctx.getRestArgsList() 243 : ctx.getNextArg(); 244 Type type = i < fixArgs ? argTypes[i-extraCount] 245 : elementType == null ? null : elementType; 246 if (type != Type.objectType) { 247 try { 248 arg = type.coerceFromObject(arg); 249 } catch (ClassCastException ex) { 250 ctx.matchError(NO_MATCH_BAD_TYPE|(i+1)); 251 return ctx; 252 } 253 } 254 if (i < fixArgs || restArray == null) { // I.e. using a LList 255 rargs[i-extraCount] = arg; 256 } else { // using array rather than LList. 257 if (type instanceof PrimType) 258 arg = ((PrimType) type).convertToRaw(arg); 259 Array.set(restArray, i - fixArgs, arg); 260 } 261 } 262 int cd = ctx.checkDone(); 263 if (cd != 0) 264 return ctx; 265 266 int arg_count = argTypes.length; 267 boolean is_constructor = isConstructor(); 268 boolean slink = is_constructor && getDeclaringClass().hasOuterLink(); 269 270 try 271 { 272 if (member == null) 273 { 274 Class clas = getDeclaringClass().getReflectClass(); 275 Class[] paramTypes = new Class[arg_count+(slink?1:0)]; 276 for (int i = arg_count; --i >= 0; ) 277 paramTypes[i+(slink?1:0)] = argTypes[i].getReflectClass(); 278 if (slink) 279 paramTypes[0] = getDeclaringClass().getOuterLinkType().getReflectClass(); 280 if (is_constructor) 281 member = clas.getConstructor(paramTypes); 282 else if (method != Type.clone_method) 283 member = clas.getMethod(method.getName(), paramTypes); 284 } 285 Object result; 286 if (is_constructor) 287 { 288 if (slink) 289 { 290 Object[] xargs = new Object[nargs]; 291 System.arraycopy(rargs, 0, xargs, 1, nargs-1); 292 xargs[0] = ((PairClassType) extraArg).staticLink; 293 rargs = xargs; 294 } 295 296 result = (((java.lang.reflect.Constructor) member) 297 .newInstance(rargs)); 298 } 299 else if (method == Type.clone_method) 300 { 301 // The special Type.clone_method is only used for array types. 302 Object arr = extraArg; 303 Class elClass = arr.getClass().getComponentType(); 304 int n = java.lang.reflect.Array.getLength(arr); 305 result = java.lang.reflect.Array.newInstance(elClass, n); 306 System.arraycopy(arr, 0, result, 0, n); 307 } 308 else 309 result = retType.coerceToObject(((java.lang.reflect.Method) member) 310 .invoke(extraArg, rargs)); 311 if (! takesContext()) 312 ctx.consumer.writeObject(result); 313 } 314 catch (java.lang.reflect.InvocationTargetException ex) 315 { 316 throw ex.getTargetException(); 317 } 318 return null; 319 } 320 321 public PrimProcedure (String className, String methodName, int numArgs) 322 { 323 this(ClassType.make(className).getDeclaredMethod(methodName, numArgs)); 324 } 325 326 public PrimProcedure(java.lang.reflect.Method method, Language language) 327 { 328 this(((ClassType) language.getTypeFor(method.getDeclaringClass())) 329 .getMethod(method), language); 330 } 331 332 public PrimProcedure(Method method) { 333 super(true, applyToConsumer); 334 init(method); 335 this.retType = method.getName().endsWith("$X") ? Type.objectType 336 : method.getReturnType(); 337 } 338 339 public PrimProcedure(Method method, Type retType, Type[] argTypes) { 340 init(method); 341 this.retType = retType; 342 if (argTypes != null) 343 this.argTypes = argTypes; 344 } 345 346 public PrimProcedure(Method method, Language language) 347 { 348 this(method, '\0', language, null); 349 } 350 351 public PrimProcedure(Method method, char mode, Language language, 352 ParameterizedType parameterizedType) { 353 super(true, applyToConsumer); 354 this.mode = mode; 355 356 init(method); 357 // This stuff deals with that a language may have its own mapping 358 // from Java types to language types, for coercions and other reasons. 359 Type[] pTypes = this.argTypes; 360 int nTypes = pTypes.length; 361 argTypes = null; 362 String[] annotTypes; 363 try { 364 SourceMethodType sourceType = method.getAnnotation(SourceMethodType.class); 365 annotTypes = sourceType == null ? null : sourceType.value(); 366 } catch (Throwable ex) { 367 annotTypes = null; 368 } 369 for (int i = nTypes; --i >= 0; ) { 370 Type javaType = pTypes[i]; 371 Type langType = decodeType(javaType, annotTypes, i+1, 372 parameterizedType, language); 373 if (javaType != langType) { 374 if (argTypes == null) { 375 argTypes = new Type[nTypes]; 376 System.arraycopy(pTypes, 0, argTypes, 0, nTypes); 377 } 378 argTypes[i] = langType; 379 } 380 } 381 if (argTypes == null) 382 argTypes = pTypes; 383 if (isConstructor()) 384 retType = getDeclaringClass(); 385 else if (method.getName().endsWith("$X")) 386 retType = Type.objectType; 387 else { 388 retType = decodeType(method.getReturnType(), 389 annotTypes, 0, parameterizedType, language); 390 // Kludge - toStringType doesn't have methods. 391 // It shouldn't be used as the "type" of anything - 392 // it's just a type with a coercion. FIXME. 393 if (retType == Type.toStringType) 394 retType = Type.javalangStringType; 395 } 396 } 397 decodeType(Type javaType, String[] annotTypes, int annotIndex, ParameterizedType parameterizedType, Language lang)398 public static Type decodeType(Type javaType, 399 String[] annotTypes, int annotIndex, 400 ParameterizedType parameterizedType, 401 Language lang) { 402 String annotType = annotTypes != null && annotTypes.length > annotIndex 403 ? annotTypes[annotIndex] : null; 404 return lang.decodeType(javaType, annotType, parameterizedType); 405 } 406 setOpcode(Method m)407 private void setOpcode(Method m) { 408 int flags = m.getModifiers(); 409 if ((flags & Access.STATIC) != 0) 410 this.op_code = 184; // invokestatic 411 else { 412 ClassType mclass = m.getDeclaringClass(); 413 if (mode == 'P') 414 this.op_code = 183; // invokespecial 415 else { 416 mode = 'V'; 417 if ("<init>".equals(m.getName())) 418 this.op_code = 183; // invokespecial 419 else if ((mclass.getModifiers() & Access.INTERFACE) != 0) 420 this.op_code = 185; // invokeinterface 421 else 422 this.op_code = 182; // invokevirtual 423 } 424 } 425 } 426 init(Method method)427 private void init(Method method) 428 { 429 this.method = method; 430 this.methodForInvoke = method; 431 setOpcode(method); 432 Type[] mtypes = method.getParameterTypes(); 433 if (isConstructor() && method.getDeclaringClass().hasOuterLink()) 434 { 435 int len = mtypes.length-1; 436 Type[] types = new Type[len]; 437 System.arraycopy(mtypes, 1, types, 0, len); 438 mtypes = types; 439 } 440 this.argTypes = mtypes; 441 } 442 PrimProcedure(Method method, LambdaExp source)443 public PrimProcedure(Method method, LambdaExp source) 444 { 445 this(method); 446 this.retType = source.getReturnType(); 447 this.source = source; 448 } 449 PrimProcedure(int opcode, Type retType, Type[] argTypes)450 public PrimProcedure(int opcode, Type retType, Type[] argTypes) 451 { 452 this.op_code = opcode; 453 this.retType = retType; 454 this.argTypes= argTypes; 455 } 456 makeBuiltinUnary(int opcode, Type type)457 public static PrimProcedure makeBuiltinUnary(int opcode, Type type) 458 { 459 // FIXME - should cache! 460 Type[] args = new Type[1]; 461 args[0] = type; 462 return new PrimProcedure(opcode, type, args); 463 } 464 makeBuiltinBinary(int opcode, Type type)465 public static PrimProcedure makeBuiltinBinary(int opcode, Type type) 466 { 467 // FIXME - should cache! 468 Type[] args = new Type[2]; 469 args[0] = type; 470 args[1] = type; 471 return new PrimProcedure(opcode, type, args); 472 } 473 PrimProcedure(int op_code, ClassType classtype, String name, Type retType, Type[] argTypes)474 public PrimProcedure(int op_code, ClassType classtype, String name, 475 Type retType, Type[] argTypes) 476 { 477 this.op_code = op_code; 478 method = classtype.addMethod (name, op_code == 184 ? Access.STATIC : 0, 479 argTypes, retType); 480 methodForInvoke = method; 481 this.retType = retType; 482 this.argTypes= argTypes; 483 mode = op_code == 184 ? '\0' : 'V'; 484 } 485 486 /** True if there is no 'this' parameter. */ getStaticFlag()487 public final boolean getStaticFlag() 488 { 489 return method == null 490 || method.getStaticFlag() 491 || isConstructor(); 492 } 493 getParameterTypes()494 public final Type[] getParameterTypes() { return argTypes; } 495 compileRestArg(Type arg_type, ApplyExp exp, int startArg, int i, Compilation comp)496 public static final void compileRestArg(Type arg_type, ApplyExp exp, int startArg, int i, Compilation comp) { 497 Expression[] args = exp.getArgs(); 498 int nargs = args.length - startArg; 499 CodeAttr code = comp.getCode(); 500 boolean argTypeIsList = arg_type == Compilation.scmListType 501 || arg_type == LangObjType.listType; 502 if (argTypeIsList) { 503 if (exp.firstSpliceArg < 0) { 504 gnu.kawa.functions.MakeList.compile(args, startArg+i, comp); 505 return; 506 } 507 // FIXME check if can use splice argument directly 508 } 509 if (startArg+i+1== args.length 510 && exp.firstSpliceArg==startArg+i) { 511 // See if final argument is a splice of an array we can re-use. 512 Expression spliceArg = MakeSplice.argIfSplice(args[startArg+i]); 513 Type spliceType = spliceArg.getType(); 514 if (spliceType instanceof ArrayType && arg_type instanceof ArrayType) { 515 Type spliceElType = ((ArrayType) spliceType).getComponentType(); 516 Type argElType = ((ArrayType) arg_type).getComponentType(); 517 if (argElType.isCompatibleWithValue(spliceElType) == 2) { 518 spliceArg.compileWithPosition(comp, Target.pushObject); 519 return; 520 } 521 } 522 if (arg_type == spliceType 523 || ((argTypeIsList || arg_type == LangObjType.argListType) 524 && (spliceType == Compilation.scmListType 525 || spliceType == LangObjType.listType))) { 526 spliceArg.compileWithPosition(comp, 527 Target.pushValue(arg_type)); 528 return; 529 } 530 } 531 if (arg_type == LangObjType.argListType 532 || arg_type == LangObjType.argVectorType) { 533 Expression[] xargs = new Expression[nargs-i]; 534 System.arraycopy(args, startArg+i, xargs, 0, xargs.length); 535 ApplyExp xexp = new ApplyExp(exp.func, xargs); 536 xexp.adjustSplice(exp, startArg+i); 537 Method setupMethod = 538 Compilation.typeCallContext.getDeclaredMethod("reset", 0); 539 comp.loadCallContext(); 540 ApplyExp.compileArgsToContext(xexp, setupMethod, comp); 541 comp.loadCallContext(); 542 ClassType ctype = 543 ClassType.make(arg_type == LangObjType.argListType 544 ? "gnu.mapping.ArgListPair" 545 : "gnu.mapping.ArgListVector"); 546 Method getArgsMethod = ctype.getDeclaredMethod("getArgs", 1); 547 code.emitInvokeStatic(getArgsMethod); 548 return; 549 } 550 551 Type el_type = arg_type instanceof ArrayType 552 ? ((ArrayType) arg_type).getComponentType() 553 : Type.objectType; 554 CompileArrays.createArray(el_type, comp, 555 args, startArg+i, args.length); 556 if (argTypeIsList) { 557 code.emitPushInt(0); 558 code.emitInvokeStatic(Compilation.makeListMethod); 559 } 560 } 561 562 /** Compile arguments and push unto stack. 563 * @param args arguments to evaluate and push. 564 * @param startArg Normally 0, but 1 in the case of a constructor, 565 * or the case of "static" method of a non-static class. 566 * @param thisType If we are calling a non-static function, 567 * then args[0] is the receiver and thisType is its expected class. 568 * If thisType==Type.voidType, ignore argTypes[0]. (It is used to to 569 * pass a link to a closure environment, which was pushed by our caller.) 570 * If thisType==null, no special handling of args[0] or argTypes[0]. 571 */ compileArgs(ApplyExp exp, int startArg, Type thisType, Compilation comp)572 private void compileArgs(ApplyExp exp, int startArg, Type thisType, Compilation comp) 573 { 574 Expression[] args = exp.getArgs(); 575 boolean variable = takesVarArgs(); 576 String name = getName(); 577 Type arg_type = null; 578 gnu.bytecode.CodeAttr code = comp.getCode(); 579 int skipArg = thisType == Type.voidType ? 1 : 0; 580 int arg_count = argTypes.length - skipArg; 581 if (takesContext()) 582 arg_count--; 583 int nargs = args.length - startArg; 584 boolean is_static = thisType == null || skipArg != 0; 585 // Do we need to check at runtime whether a final argument to a VARARGS 586 // is an array or need to be wrapped in an array? 587 // See comment at explicitArrayAsVarArgsAllowed. 588 boolean createVarargsArrayIfNeeded = false; 589 if (explicitArrayAsVarArgsAllowed 590 && variable && (method.getModifiers() & Access.VARARGS) != 0 591 && exp.firstSpliceArg < 0 592 && nargs > 0 && argTypes.length > 0 593 && nargs == arg_count + (is_static ? 0 : 1)) 594 { 595 Type lastType = args[args.length-1].getType(); 596 Type lastParam = argTypes[argTypes.length-1]; 597 if (lastParam.isCompatibleWithValue(lastType) >= 0) 598 { 599 if (lastParam instanceof ArrayType // should always be true 600 && (((ArrayType) lastParam).getComponentType()).isCompatibleWithValue(lastType) >= 0) 601 createVarargsArrayIfNeeded = true; 602 variable = false; 603 } 604 } 605 int fix_arg_count = variable ? arg_count - (is_static ? 1 : 0) : args.length - startArg; 606 Declaration argDecl = source == null ? null : source.firstDecl(); 607 if (argDecl != null && argDecl.isThisParameter()) 608 argDecl = argDecl.nextDecl(); 609 for (int i = 0; ; ++i) 610 { 611 if (variable && i == fix_arg_count) 612 { 613 arg_type = argDecl != null ? argDecl.getType() 614 : argTypes[arg_count-1+skipArg]; 615 compileRestArg(arg_type, exp, startArg, i, comp); 616 i = nargs; 617 break; 618 } 619 if (i >= nargs) 620 break; 621 boolean createVarargsNow = createVarargsArrayIfNeeded && i + 1 == nargs; 622 if (i >= fix_arg_count) 623 { 624 code.emitDup(1); // dup array. 625 code.emitPushInt(i - fix_arg_count); 626 } 627 else 628 arg_type = argDecl != null && (is_static || i > 0) ? argDecl.getType() 629 : is_static ? argTypes[i + skipArg] 630 : i==0 ? thisType 631 : argTypes[i-1]; 632 comp.usedClass(arg_type); 633 Type argTypeForTarget = createVarargsNow ? Type.objectType : arg_type; 634 Target target = 635 source == null ? CheckedTarget.getInstance(argTypeForTarget, name, i+1) 636 : CheckedTarget.getInstance(argTypeForTarget, source, i); 637 args[startArg+i].compileWithPosition(comp, target); 638 if (createVarargsNow) // Only if explicitArrayAsVarArgsAllowed 639 { 640 // Wrap final argument in array if not already an array: 641 // Emit: (arg instanceof T[] ? (T[]) arg : new T[]{arg}) 642 Type eltype = ((ArrayType) arg_type).getComponentType(); 643 code.emitDup(); 644 code.emitInstanceof(arg_type); 645 code.emitIfIntNotZero(); 646 code.emitCheckcast(arg_type); 647 code.emitElse(); 648 code.emitPushInt(1); 649 code.emitNewArray(eltype); // Stack: value array 650 code.emitDupX(); // Stack: array value array 651 code.emitSwap(); // Stack: array array value 652 code.emitPushInt(0); // Stack array array value 0 653 code.emitSwap(); // Stack: array array 0 value 654 eltype.emitCoerceFromObject(code); 655 code.emitArrayStore(eltype); // Stack: array 656 code.emitFi(); 657 } 658 if (i >= fix_arg_count) 659 code.emitArrayStore(arg_type); 660 if (argDecl != null && (is_static || i > 0)) { 661 if (argDecl.getFlag(Declaration.IS_SUPPLIED_PARAMETER)) 662 argDecl = argDecl.nextDecl(); 663 argDecl = argDecl.nextDecl(); 664 } 665 } 666 } 667 canCompile(ApplyExp exp)668 public boolean canCompile(ApplyExp exp) { 669 if (exp.firstKeywordArgIndex != 0) 670 return false; 671 // We can optimize splice arguments if they're in the "varargs" 672 // section of the argument list. 673 if (exp.firstSpliceArg >= 0 674 && (! takesVarArgs() || minArgs() > exp.firstSpliceArg)) 675 return false; 676 return true; 677 } 678 compile(ApplyExp exp, Compilation comp, Target target)679 public boolean compile(ApplyExp exp, Compilation comp, Target target) { 680 if (! canCompile(exp)) 681 return false; 682 683 gnu.bytecode.CodeAttr code = comp.getCode(); 684 ClassType mclass = getDeclaringClass(); 685 Expression[] args = exp.getArgs(); 686 if (isConstructor()) 687 { 688 if (exp.getFlag(ApplyExp.MAY_CONTAIN_BACK_JUMP) 689 // See https://bugs.openjdk.java.net/browse/JDK-8046233 690 // The fix was released in JDK 8u25. 691 && Compilation.defaultClassFileVersion <= ClassType.JDK_1_8_VERSION) 692 { 693 // JVM spec for Java6: 694 // "There must never be an uninitialized class instance 695 // on the operand stack or in a local variable when 696 // any backwards branch is taken." 697 // Hence re-write: 698 // (make Foo a1 backward_jump_containing_expression a3 ...) 699 // to: 700 // (let ((t1 a1) 701 // (t2 backward_jump_containing_expression) 702 // (t3 q2) ...) 703 // (make Foo a1 t1 t2 t3 ...)) 704 int nargs = args.length; 705 comp.letStart(); 706 Expression[] xargs = new Expression[nargs]; 707 xargs[0] = args[0]; 708 for (int i = 1; i < nargs; i++) 709 { 710 Expression argi = args[i]; 711 // A modest optimzation: Don't generate temporary if not needed. 712 // But this also avoids a possible VerifyError in the case 713 // of LambdaExp, since if we set the latter's nameDecl to the 714 // new temporary, loading the temporary can get confused. 715 if (! (argi instanceof QuoteExp 716 || argi instanceof LambdaExp 717 || argi instanceof ReferenceExp)) 718 { 719 Declaration d = comp.letVariable(null, argi.getType(), argi); 720 d.setCanRead(true); 721 argi = new ReferenceExp(d); 722 } 723 xargs[i] = argi; 724 } 725 comp.letEnter(); 726 LetExp let = comp.letDone(new ApplyExp(exp.func, xargs)); 727 let.compile(comp, target); 728 return true; 729 } 730 code.emitNew(mclass); 731 code.emitDup(mclass); 732 } 733 int spliceCount = exp.spliceCount(); 734 String arg_error = WrongArguments.checkArgCount(this, 735 args.length-spliceCount, 736 spliceCount>0); 737 if (arg_error != null) 738 comp.error('e', arg_error); 739 740 compile(getStaticFlag() ? null : mclass, exp, comp, target); 741 return true; 742 } 743 compile(Type thisType, ApplyExp exp, Compilation comp, Target target)744 void compile (Type thisType, ApplyExp exp, Compilation comp, Target target) 745 { 746 Expression[] args = exp.getArgs(); 747 gnu.bytecode.CodeAttr code = comp.getCode(); 748 int startArg = 0; 749 if (isConstructor()) 750 { 751 ClassType mclass = getDeclaringClass(); 752 if (mclass.hasOuterLink()) 753 { 754 ClassExp.loadSuperStaticLink(args[0], mclass, comp); 755 } 756 thisType = null; 757 startArg = 1; 758 } 759 // Handle: (invoke-special ThisClass (this) '*init* arg ....) 760 // (This test is perhaps not quote as robust as it should be.) 761 else if (opcode() == 183 && mode == 'P' && "<init>".equals(method.getName())) 762 { 763 // Specifically handle passing a static-link. 764 ClassType mclass = getDeclaringClass(); 765 if (mclass.hasOuterLink()) 766 { 767 code.emitPushThis(); 768 // Push the incoming static-link. 769 code.emitLoad(code.getCurrentScope().getVariable(1)); 770 thisType = null; 771 startArg = 1; 772 } 773 } 774 else if (takesTarget() && method.getStaticFlag()) 775 startArg = 1; 776 compileArgs(exp, startArg, thisType, comp); 777 778 if (method == null) 779 { 780 code.emitPrimop (opcode(), args.length, retType); 781 target.compileFromStack(comp, retType); 782 } 783 else 784 { 785 compileInvoke(comp, methodForInvoke, target, 786 exp.isTailCall(), op_code, retType, doFixUnsigned); 787 } 788 } 789 790 /** Emit the actual invoke operation, after arguments have been pushed. 791 * Does whatever magic is needed to pass the result to target, 792 * including passing CallContext or special handling of ConsumerTarget. 793 */ 794 public static void compileInvoke(Compilation comp, Method method, Target target, boolean isTailCall, int op_code, Type returnType, boolean doFixUnsigned)795 compileInvoke (Compilation comp, Method method, Target target, 796 boolean isTailCall, int op_code, Type returnType, 797 boolean doFixUnsigned) 798 { 799 CodeAttr code = comp.getCode(); 800 comp.usedClass(method.getDeclaringClass()); 801 comp.usedClass(method.getReturnType()); 802 if (! takesContext(method)) 803 { 804 code.emitInvokeMethod(method, op_code); 805 } 806 else if (target instanceof IgnoreTarget 807 || (target instanceof ConsumerTarget 808 && ((ConsumerTarget) target).isContextTarget())) 809 { 810 Field consumerFld = null; 811 Variable saveCallContext = null; 812 comp.loadCallContext(); 813 if (target instanceof IgnoreTarget) 814 { 815 ClassType typeCallContext = Compilation.typeCallContext; 816 consumerFld = typeCallContext.getDeclaredField("consumer"); 817 818 // Consumer saveConsumer = ctx.consumer; 819 // ctx.consumer = VoidConsumer.instance: 820 code.pushScope(); 821 saveCallContext = code.addLocal(typeCallContext); 822 code.emitDup(); 823 code.emitGetField(consumerFld); 824 code.emitStore(saveCallContext); 825 code.emitDup(); 826 code.emitGetStatic(ClassType.make("gnu.lists.VoidConsumer") 827 .getDeclaredField("instance")); 828 code.emitPutField(consumerFld); 829 } 830 code.emitInvokeMethod(method, op_code); 831 ApplyExp.finishTrampoline(isTailCall, target, comp); 832 if (target instanceof IgnoreTarget) 833 { 834 // ctx.consumer = saveConsumer 835 comp.loadCallContext(); 836 code.emitLoad(saveCallContext); 837 code.emitPutField(consumerFld); 838 code.popScope(); 839 } 840 return; 841 } 842 else 843 { 844 comp.loadCallContext(); 845 returnType = Type.objectType; 846 code.pushScope(); 847 Variable saveIndex = code.addLocal(Type.intType); 848 849 // FIXME: It would be better (unless we're in a try block) to put 850 // the cleanupFromContext handler inside a fragment (moved to the 851 // end of the Code attribute). See CheckedTarget#emitCheckedCoerce 852 comp.loadCallContext(); 853 code.emitInvokeVirtual(Compilation.typeCallContext. 854 getDeclaredMethod("startFromContext", 0)); 855 code.emitStore(saveIndex); 856 code.emitWithCleanupStart(); 857 code.emitInvokeMethod(method, op_code); 858 code.emitWithCleanupCatch(null); 859 comp.loadCallContext(); 860 code.emitLoad(saveIndex); 861 code.emitInvokeVirtual(Compilation.typeCallContext. 862 getDeclaredMethod("cleanupFromContext", 1)); 863 code.emitWithCleanupDone(); 864 comp.loadCallContext(); 865 code.emitLoad(saveIndex); 866 code.emitInvokeVirtual(Compilation.typeCallContext. 867 getDeclaredMethod("getFromContext", 1)); 868 code.popScope(); 869 } 870 if (method.getReturnType() == Type.neverReturnsType) 871 { 872 // Currently only go here if !takesContext(method). FIXME: We should 873 // use annotations or something to figure out return type of methods 874 // that take a context (and whose return type is thus void). 875 compileReachedUnexpected(code); 876 } 877 else { 878 if (method.getReturnType() instanceof TypeVariable) { 879 if (returnType instanceof ClassType) { 880 // This avoids an unneeded exception handler (if the 881 // target is a CheckedTarget), and a needless call to 882 // Promise.force. Also, if the target.getType() is 883 // different from the returnType, it's probably better 884 // to convert to returnType first - it might make it 885 // easier to find the right conversion, or emit a more 886 // accurate error message. 887 code.emitCheckcast(returnType); 888 } else 889 returnType = method.getReturnType().getRawType(); 890 } 891 if (doFixUnsigned) 892 code.fixUnsigned(returnType); 893 target.compileFromStack(comp, returnType); 894 } 895 } 896 compileReachedUnexpected(CodeAttr code)897 public static void compileReachedUnexpected(CodeAttr code) { 898 if (code.reachableHere()) { 899 code.emitGetStatic(ClassType.make("gnu.expr.Special") 900 .getDeclaredField("reachedUnexpected")); 901 code.emitThrow(); 902 } 903 } 904 getParameterType(int index)905 public Type getParameterType(int index) 906 { 907 if (takesTarget()) 908 { 909 if (index == 0) 910 return isConstructor() ? Type.objectType 911 : getDeclaringClass(); 912 index--; 913 } 914 int lenTypes = argTypes.length; 915 if (index < lenTypes - 1) 916 return argTypes[index]; 917 boolean varArgs = takesVarArgs(); 918 if (index < lenTypes && ! varArgs) 919 return argTypes[index]; 920 Type restType = argTypes[lenTypes - 1]; 921 if (restType instanceof ArrayType) 922 return ((ArrayType) restType).getComponentType(); 923 else // Should be LList or some other Sequence class. 924 return Type.objectType; 925 } 926 927 // This is null in JDK 1.1 and something else in JDK 1.2. 928 private static ClassLoader systemClassLoader 929 = PrimProcedure.class.getClassLoader(); 930 931 /** Return the index of the most specific method. 932 * An approximation of the algorithm in JLS3 933 * 15.12.2.5 "Choosing the Most Specific Method." */ mostSpecific(PrimProcedure[] procs, int length)934 public static int mostSpecific(PrimProcedure[] procs, int length) { 935 if (length <= 1) // Handles length==0 and length==1. 936 return length - 1; 937 // best is non-negative if there is a single most specific method. 938 int best = 0; 939 // This array (which is allocated lazily) is used if there is is a 940 // set of bestn methods none of which are more specific 941 // than the others. 942 int[] bests = null; 943 // The active length of the bests array. 944 int bestn = 0; 945 outer: 946 for (int i = 1; i < length; i++) { 947 PrimProcedure method = procs[i]; 948 if (best >= 0) { 949 PrimProcedure winner 950 = (PrimProcedure) mostSpecific(procs[best], method); 951 if (winner == null) { 952 if (bests == null) 953 bests = new int[length]; 954 bests[0] = best; 955 bests[1] = i; 956 bestn = 2; 957 best = -1; 958 } else if (winner == method) { 959 best = i; 960 bestn = i; 961 } 962 } else { 963 for (int j = 0; j < bestn; j++) { 964 PrimProcedure old = procs[bests[j]]; 965 PrimProcedure winner 966 = (PrimProcedure) mostSpecific(old, method); 967 if (winner == old) 968 continue outer; 969 if (winner == null) { 970 bests[bestn++] = i; 971 continue outer; 972 } 973 } 974 // At this point method is more specific than bests[0..bestn-1]. 975 best = i; 976 bestn = i; 977 } 978 } 979 if (best < 0 && bestn > 1) { 980 PrimProcedure first = procs[bests[0]]; 981 for (int j = 0; j < bestn; j++) { 982 int m = bests[j]; 983 PrimProcedure method = procs[m]; 984 if (j > 0 && ! overrideEquivalent(first, method)) 985 return -1; 986 if (! method.isAbstract()) { 987 if (best >= 0) 988 return -1; 989 best = m; 990 } 991 } 992 return best >= 0 ? best : bests[0]; 993 } 994 return best; 995 } 996 getMethodFor(Procedure pproc, Expression[] args)997 public static PrimProcedure getMethodFor (Procedure pproc, Expression[] args) 998 { 999 return getMethodFor(pproc, null, args, Language.getDefaultLanguage()); 1000 } 1001 1002 /** Search for a matching static method in a procedure's class. 1003 * @return a PrimProcedure that is suitable, or null. */ getMethodFor(Procedure pproc, Declaration decl, Expression[] args, Language language)1004 public static PrimProcedure getMethodFor (Procedure pproc, Declaration decl, 1005 Expression[] args, 1006 Language language) 1007 { 1008 int nargs = args.length; 1009 Type[] atypes = new Type[nargs]; 1010 for (int i = nargs; --i >= 0;) atypes[i] = args[i].getType(); 1011 return getMethodFor(pproc, decl, atypes, language); 1012 } 1013 getMethodFor(Procedure pproc, Declaration decl, Type[] atypes, Language language)1014 public static PrimProcedure getMethodFor (Procedure pproc, Declaration decl, 1015 Type[] atypes, Language language) 1016 { 1017 if (pproc instanceof GenericProc) 1018 { 1019 GenericProc gproc = (GenericProc) pproc; 1020 MethodProc[] methods = gproc.methods; 1021 pproc = null; 1022 for (int i = gproc.count; --i >= 0; ) 1023 { 1024 int applic = methods[i].isApplicable(atypes, null/*FIXME*/); 1025 if (applic < 0) 1026 continue; 1027 if (pproc != null) 1028 return null; // Ambiguous. 1029 pproc = methods[i]; 1030 } 1031 if (pproc == null) 1032 return null; 1033 } 1034 if (pproc instanceof PrimProcedure) 1035 { 1036 PrimProcedure prproc = (PrimProcedure) pproc; 1037 if (prproc.isApplicable(atypes, null/*FIXME*/) >= 0) 1038 return prproc; 1039 } 1040 Class pclass = getProcedureClass(pproc); 1041 if (pclass == null) 1042 return null; 1043 return getMethodFor((ClassType) Type.make(pclass), pproc.getName(), 1044 decl, atypes, language); 1045 } 1046 disassemble$X(Procedure pproc, CallContext ctx)1047 public static void disassemble$X (Procedure pproc, CallContext ctx) 1048 throws Exception 1049 { 1050 gnu.lists.Consumer cons = ctx.consumer; 1051 disassemble(pproc, cons instanceof Writer ? (Writer) cons: new ConsumerWriter(cons)); 1052 } 1053 disassemble(Procedure proc, Writer out)1054 public static void disassemble (Procedure proc, Writer out) 1055 throws Exception 1056 { 1057 disassemble(proc, new ClassTypeWriter(null, out, 0)); 1058 } 1059 disassemble(Procedure proc, ClassTypeWriter cwriter)1060 public static void disassemble (Procedure proc, ClassTypeWriter cwriter) 1061 throws Exception 1062 { 1063 if (proc instanceof GenericProc) 1064 { 1065 GenericProc gproc = (GenericProc) proc; 1066 int n = gproc.getMethodCount(); 1067 cwriter.print("Generic procedure with "); 1068 cwriter.print(n); 1069 cwriter.println(n == 1 ? " method." : "methods."); 1070 for (int i = 0; i < n; i++) 1071 { 1072 Procedure mproc = gproc.getMethod(i); 1073 if (mproc != null) 1074 { 1075 cwriter.println(); 1076 disassemble(mproc, cwriter); 1077 } 1078 } 1079 return; 1080 } 1081 String pname = null; 1082 String aname = null; // apply method name, or null 1083 int alength = -1; // aname length, with ""$check" 1084 Class cl = proc.getClass(); 1085 if (proc instanceof CompiledProc) { 1086 CompiledProc cp = (CompiledProc) proc; 1087 /* #ifdef JAVA8 */ 1088 try { 1089 MethodHandleInfo minfo = 1090 java.lang.invoke.MethodHandles.lookup() 1091 .revealDirect(cp.getApplyMethod()); 1092 cl = minfo.getDeclaringClass(); 1093 pname = minfo.getName(); 1094 if (pname.endsWith("$check")) { 1095 aname = pname; 1096 alength = aname.length()-6; 1097 pname = pname.substring(0, alength); 1098 } 1099 } catch (Exception ex) { 1100 cl = cp.getModuleClass(); 1101 } 1102 /* #else */ 1103 // cl = cp.getModuleClass(); 1104 /* #endif */ 1105 } else if (proc instanceof PrimProcedure) 1106 { 1107 Method pmethod = ((PrimProcedure) proc).methodForInvoke; 1108 if (pmethod != null) 1109 { 1110 cl = pmethod.getDeclaringClass().getReflectClass(); 1111 pname = pmethod.getName(); 1112 } 1113 } 1114 ClassLoader loader = cl.getClassLoader(); 1115 if (loader == null) loader = ClassLoader.getSystemClassLoader(); 1116 String cname = cl.getName(); 1117 String rname = cname.replace('.', '/') + ".class"; 1118 ClassType ctype = new ClassType(); 1119 java.io.InputStream rin = loader.getResourceAsStream(rname); 1120 if (rin == null) 1121 throw new RuntimeException("missing resource "+rname); // FIXME exception 1122 ClassFileInput cinput = new ClassFileInput(ctype, rin); 1123 cwriter.setClass(ctype); 1124 java.net.URL resource = loader.getResource(rname); 1125 cwriter.print("In class "); 1126 cwriter.print(cname); 1127 if (resource != null) 1128 { 1129 cwriter.print(" at "); 1130 cwriter.print(resource); 1131 } 1132 cwriter.println(); 1133 if (pname == null) 1134 { 1135 pname = proc.getName(); 1136 if (pname == null) 1137 { 1138 cwriter.println("Anonymous function - unknown method."); 1139 return; 1140 } 1141 pname = Mangling.mangleName(pname); 1142 } 1143 for (Method method = ctype.getMethods(); 1144 method != null; method = method.getNext()) 1145 { 1146 String mname = method.getName(); 1147 if ((aname != null && mname.length() >= alength 1148 && mname.substring(0, alength).equals(pname)) 1149 || mname.equals(pname)) { 1150 cwriter.printMethod(method); 1151 } 1152 } 1153 cwriter.flush(); 1154 } 1155 getProcedureClass(Object pproc)1156 public static Class getProcedureClass (Object pproc) 1157 { 1158 Class procClass; 1159 if (pproc instanceof CompiledProc) 1160 procClass = ((CompiledProc) pproc).getModuleClass(); 1161 else 1162 procClass = pproc.getClass(); 1163 try 1164 { 1165 if (procClass.getClassLoader() == systemClassLoader) 1166 return procClass; 1167 } 1168 catch (SecurityException ex) 1169 { 1170 } 1171 return null; 1172 } 1173 1174 /** Get PrimProcedure for matching method in given class. */ 1175 public static PrimProcedure getMethodFor(Class procClass, String name, Declaration decl, Expression[] args, Language language)1176 getMethodFor (Class procClass, String name, Declaration decl, 1177 Expression[] args, Language language) 1178 { 1179 return getMethodFor((ClassType) Type.make(procClass), 1180 name, decl, args, language); 1181 } 1182 1183 public static PrimProcedure getMethodFor(ClassType procClass, String name, Declaration decl, Expression[] args, Language language)1184 getMethodFor (ClassType procClass, String name, Declaration decl, 1185 Expression[] args, Language language) 1186 { 1187 int nargs = args.length; 1188 Type[] atypes = new Type[nargs]; 1189 for (int i = nargs; --i >= 0;) atypes[i] = args[i].getType(); 1190 return getMethodFor(procClass, name, decl, atypes, language); 1191 } 1192 1193 public static PrimProcedure getMethodFor(ClassType procClass, String name, Declaration decl, Type[] atypes, Language language)1194 getMethodFor (ClassType procClass, String name, Declaration decl, 1195 Type[] atypes, Language language) 1196 { 1197 PrimProcedure best = null; 1198 int bestCode = -1; 1199 boolean bestIsApply = false; 1200 try 1201 { 1202 if (name == null) 1203 return null; 1204 String mangledName = Mangling.mangleName(name); 1205 String mangledNameV = mangledName + "$V"; 1206 String mangledNameVX = mangledName + "$V$X"; 1207 String mangledNameX = mangledName + "$X"; 1208 boolean applyOk = true; // Also look for "apply" and "apply$V". 1209 for (Method meth = procClass.getDeclaredMethods(); 1210 meth != null; meth = meth.getNext()) 1211 { 1212 int mods = meth.getModifiers(); 1213 if ((mods & (Access.STATIC|Access.PUBLIC)) 1214 != (Access.STATIC|Access.PUBLIC)) 1215 { 1216 if (decl == null || decl.base == null) 1217 continue; 1218 } 1219 String mname = meth.getName(); 1220 boolean isApply; 1221 if (mname.equals(mangledName) 1222 || mname.equals(mangledNameV) 1223 || mname.equals(mangledNameX) 1224 || mname.equals(mangledNameVX)) 1225 { 1226 isApply = false; 1227 } 1228 else if (applyOk 1229 && (mname.equals("apply") || mname.equals("apply$V"))) 1230 { 1231 isApply = true; 1232 } 1233 else 1234 continue; 1235 if (! isApply) 1236 { 1237 // If we saw a real match, ignore "apply". 1238 applyOk = false; 1239 if (bestIsApply) 1240 { 1241 best = null; 1242 bestCode = -1; 1243 bestIsApply = false; 1244 } 1245 } 1246 PrimProcedure prproc = new PrimProcedure(meth, language); 1247 prproc.setName(name); 1248 int code = prproc.isApplicable(atypes, null/*FIXME*/); 1249 if (code < 0 || code < bestCode) 1250 continue; 1251 if (code > bestCode) 1252 { 1253 best = prproc; 1254 } 1255 else if (best != null) 1256 { 1257 best = (PrimProcedure) MethodProc.mostSpecific(best, prproc); 1258 if (best == null) 1259 { // Ambiguous. 1260 if (bestCode > 0) 1261 return null; 1262 } 1263 } 1264 bestCode = code; 1265 bestIsApply = isApply; 1266 } 1267 } 1268 catch (SecurityException ex) 1269 { 1270 } 1271 return best; 1272 } 1273 getName()1274 public String getName() 1275 { 1276 String name = super.getName(); 1277 if (name != null) 1278 return name; 1279 name = getVerboseName(); 1280 setName(name); 1281 return name; 1282 } 1283 getVerboseName()1284 public String getVerboseName() { 1285 StringBuilder buf = new StringBuilder(100); 1286 if (method == null) { 1287 buf.append("<op "); 1288 buf.append(op_code); 1289 buf.append('>'); 1290 } else { 1291 buf.append(getDeclaringClass().getName()); 1292 buf.append('.'); 1293 buf.append(method.getName()); 1294 } 1295 if (argTypes != null) { 1296 buf.append('('); 1297 for (int i = 0; i < argTypes.length; i++) { 1298 if (i > 0) 1299 buf.append(','); 1300 buf.append(argTypes[i].getName()); 1301 } 1302 buf.append(')'); 1303 } 1304 return buf.toString(); 1305 } 1306 toString()1307 public String toString() 1308 { 1309 StringBuffer buf = new StringBuffer(100); 1310 buf.append(retType == null ? "<unknown>" : retType/*.getName()*/); 1311 buf.append(' '); 1312 buf.append(getVerboseName()); 1313 return buf.toString(); 1314 } 1315 print(java.io.PrintWriter ps)1316 public void print(java.io.PrintWriter ps) 1317 { 1318 ps.print("#<primitive procedure "); 1319 ps.print(toString()); 1320 ps.print ('>'); 1321 } 1322 1323 public static final MethodHandle applyToConsumer 1324 = lookupApplyHandle(PrimProcedure.class, "applyToConsumer"); 1325 } 1326