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