1 package gnu.kawa.functions;
2 import gnu.bytecode.*;
3 import gnu.mapping.*;
4 import gnu.math.*;
5 import gnu.expr.*;
6 import gnu.kawa.reflect.*;
7 import gnu.kawa.lispexpr.LangObjType;
8 import gnu.lists.LList;
9 import kawa.standard.Scheme;
10 import kawa.standard.SchemeCompilation;
11 import static gnu.expr.InlineCalls.LenientExpectedType;
12 
13 public class CompileMisc
14 {
validateApplyConstantFunction0(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)15   public static Expression validateApplyConstantFunction0
16   (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
17   {
18     exp.visitArgs(visitor);
19     int nargs = exp.getArgCount();
20     if (nargs != 0 && visitor != null)
21       {
22         String message = WrongArguments.checkArgCount(proc, nargs, false);
23 	return visitor.noteError(message);
24       }
25     return ((ConstantFunction0) proc).constant;
26   }
27 
validateApplyConvert(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)28   public static Expression validateApplyConvert
29   (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
30   {
31     Compilation comp = visitor.getCompilation();
32     Language language = comp.getLanguage();
33     Convert cproc = (Convert) proc;
34     Expression[] args = exp.getArgs();
35     if (args.length == 2)
36       {
37         args[0] = visitor.visit(args[0], null);
38         Type type = language.getTypeFor(args[0]);
39         if (type instanceof Type)
40           {
41             args[0] = new QuoteExp(type);
42             Type xtype = cproc.lenient ? LenientExpectedType.make(type)
43                 : type;
44             if (! args[1].getFlag(Expression.VALIDATED))
45                 args[1] = ExpVisitor.visit(visitor, args[1], xtype);
46             args[1] = visitor.checkType(args[1], xtype);
47             CompileReflect.checkKnownClass(type, comp);
48             exp.setType(type);
49             Type argType = args[1].getType();
50             if (argType != Type.nullType
51                 && type.isCompatibleWithValue(argType) == 2)
52                 return args[1];
53             return exp;
54           }
55       }
56     exp.visitArgs(visitor);
57     return exp;
58   }
59 
validateApplySimpleBoolean(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)60   public static Expression validateApplySimpleBoolean
61   (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
62   {
63     exp.visitArgs(visitor);
64     exp.setType(visitor.getCompilation().getLanguage().getTypeFor(Boolean.TYPE));
65     return exp.inlineIfConstant(proc, visitor);
66   }
67 
68   /** Validate-apply handling for "format".
69    * Sets the correct return-type, and may replace by call to a static method.
70    */
validateApplyFormat(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)71   public static Expression validateApplyFormat
72   (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
73   {
74     exp.visitArgs(visitor);
75     Type retType = Type.objectType;
76     Expression[] args = exp.getArgs();
77     if (args.length > 0)
78       {
79         ClassType typeFormat = ClassType.make("gnu.kawa.functions.Format");
80         Object f = args[0].valueIfConstant();
81         Type ftype = args[0].getType();
82         if (f == Boolean.FALSE || ftype.isSubtype(LangObjType.stringType))
83           {
84             int skip = f == Boolean.FALSE ? 1 : 0;
85             if (skip > 0 && args.length > 1)
86                  f = args[1].valueIfConstant();
87             if (f instanceof CharSequence && ! (f instanceof String)) {
88                 f = f.toString();
89                 args[skip] = QuoteExp.getInstance(f);
90             }
91             Expression[] xargs = new Expression[args.length+1-skip];
92             xargs[0] = new QuoteExp(Integer.valueOf(0), Type.intType);
93             System.arraycopy(args, skip, xargs, 1, xargs.length-1);
94             ApplyExp ae = new ApplyExp(typeFormat.getDeclaredMethod("formatToString", 2), xargs);
95             ae.setLine(exp);
96             ae.setType(Type.javalangStringType);
97             return ae;
98           }
99         if ((f == Boolean.TRUE || ftype.isSubtype(ClassType.make("java.io.Writer")))
100             && args.length > 1)
101           {
102             if (f == Boolean.TRUE)
103               {
104                 Expression[] xargs = new Expression[args.length];
105                 xargs[0] = QuoteExp.nullExp;
106                 System.arraycopy(args, 1, xargs, 1, args.length-1);
107                 args = xargs;
108               }
109             f = args[1].valueIfConstant();
110             if (f instanceof CharSequence && ! (f instanceof String)) {
111                 f = f.toString();
112                 args[1] = QuoteExp.getInstance(f);
113             }
114             ApplyExp ae = new ApplyExp(typeFormat.getDeclaredMethod("formatToWriter", 3), args);
115             ae.setLine(exp);
116             ae.setType(Type.voidType);
117             return ae;
118           }
119         if (ftype.isSubtype(ClassType.make("java.io.OutputStream")))
120           retType = Type.voidType;
121       }
122     exp.setType(retType);
123     return null;
124   }
125 
validateApplyAppendValues(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)126   public static Expression validateApplyAppendValues
127   (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
128   {
129     exp.visitArgs(visitor);
130     Expression[] args = exp.getArgs();
131     int nargs = args.length;
132     if (nargs == 1)
133       return args[0];
134     if (nargs == 0)
135         return QuoteExp.voidExp;
136     Expression folded = exp.inlineIfConstant(proc, visitor);
137     if (folded.valueIfConstant() == Values.empty)
138         folded = QuoteExp.voidObjectExp;
139     else if (folded == exp) {
140         args = exp.getArgs();
141         Type typeSoFar = Type.voidType;
142         for (int i = 0;  i < nargs;  i++) {
143             Type atype = args[i].getType();
144             if (OccurrenceType.itemCountCode(atype) == '0')
145                 continue;
146             if (typeSoFar == Type.voidType)
147                 typeSoFar = atype;
148             else
149                 typeSoFar = Type.objectType; // FIXME - can do better.
150         }
151         exp.setType(typeSoFar);
152     }
153     return folded;
154   }
155 
validateApplyMakeProcedure(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)156   public static Expression validateApplyMakeProcedure
157   (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
158   {
159     exp.visitArgs(visitor);
160     Expression[] args = exp.getArgs();
161     int alen = args.length;
162     Expression method = null;
163     int countMethods = 0;
164     String name = null;
165     for (int i = 0;  i < alen;  i++)
166       {
167 	Expression arg = args[i];
168         Keyword key = arg.checkLiteralKeyword();
169 	if (key != null)
170 	  {
171 	    String keyword = key.getName();
172 	    Expression next = args[++i];
173 	    if (keyword == "name")
174               {
175                 if (next instanceof QuoteExp)
176                   name = ((QuoteExp) next).getValue().toString();
177               }
178 	    else if (keyword == "method")
179               {
180                 countMethods++;
181                 method = next;
182               }
183 	    else
184 	      ; // result.setProperty(keyword, value);
185 	  }
186 	else
187           {
188             countMethods++;
189             method = arg;
190           }
191       }
192     if (countMethods == 1 && method instanceof LambdaExp)
193       {
194         LambdaExp lexp = (LambdaExp) method;
195         for (int i = 0;  i < alen;  i++)
196           {
197             Expression arg = args[i];
198             Keyword key = arg.checkLiteralKeyword();
199             if (key != null)
200               {
201                 String keyword = key.getName();
202                 Expression next = args[++i];
203                 if (keyword == "name")
204                   lexp.setName(name);
205                 else if (keyword == "method")
206                   ;
207                 else
208                   lexp.setProperty(Namespace.EmptyNamespace.getSymbol(keyword), next);
209               }
210           }
211         return method;
212       }
213     return exp;
214   }
215 
validateApplyValuesMap(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)216   public static Expression validateApplyValuesMap
217   (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
218   {
219     LambdaExp lexp = ValuesMap.canInline(exp, (ValuesMap) proc);
220     // FIXME If required is an OccurrenceType, then we want to validate
221     // lexp.body with the same OccurrenceType.  This is tricky - best to
222     // do a tree rewrite here, instead of in ValuesMap#compile.
223     exp.visitArgs(visitor);
224     if (lexp != null)
225         lexp.setInlineOnly(exp, visitor.getCurrentLambda());
226     return exp;
227   }
228 
229   static gnu.bytecode.ClassType typeType;
230   static gnu.bytecode.Method coerceMethod;
231 
compileConvert(ApplyExp exp, Compilation comp, Target target, Procedure procedure)232     public static boolean compileConvert (ApplyExp exp, Compilation comp,
233                                           Target target, Procedure procedure) {
234         Convert proc = (Convert) procedure;
235         Expression[] args = exp.getArgs();
236         if (args.length != 2 || ! exp.isSimple())
237             return false;
238         CodeAttr code = comp.getCode();
239         Type type = Scheme.getTypeValue(args[0]);
240         if (type == Type.neverReturnsType) {
241             args[1].compile(comp, Target.Ignore);
242             PrimProcedure.compileReachedUnexpected(code);
243         } else if (type != null) {
244             args[1].compile(comp, Target.pushValue(type));
245             if (code.reachableHere() && ! type.isVoid()) {
246                 target.compileFromStack(comp, type);
247             }
248         } else {
249             if (typeType == null) {
250                 typeType = ClassType.make("gnu.bytecode.Type");
251             }
252             if (coerceMethod == null) {
253                 coerceMethod = typeType.addMethod("coerceFromObject",
254                                                   Compilation.apply1args,
255                                                   Type.pointer_type,
256                                                   gnu.bytecode.Access.PUBLIC);
257             }
258             args[0].compile(comp, LangObjType.typeClassType);
259             args[1].compile(comp, Target.pushObject);
260             code.emitInvokeVirtual(coerceMethod);
261             target.compileFromStack(comp, Type.pointer_type);
262         }
263         return true;
264     }
265 
compileNot(ApplyExp exp, Compilation comp, Target target, Procedure procedure)266     public static boolean compileNot(ApplyExp exp, Compilation comp,
267                                      Target target, Procedure procedure) {
268         if (! exp.isSimple())
269             return false;
270         Not proc = (Not) procedure;
271         Expression arg = exp.getArgs()[0];
272         Language language = proc.language;
273         if (target instanceof ConditionalTarget) {
274             ConditionalTarget ctarget = (ConditionalTarget) target;
275             ConditionalTarget sub_target
276                 = new ConditionalTarget(ctarget.ifFalse, ctarget.ifTrue,
277                                         language);
278             sub_target.trueBranchComesFirst = ! ctarget.trueBranchComesFirst;
279             arg.compile(comp, sub_target);
280             return true;
281         }
282         CodeAttr code = comp.getCode();
283         Type type = target.getType();
284         QuoteExp trueExp, falseExp;
285         if (target instanceof StackTarget
286                 && type.getSignature().charAt(0) == 'Z') {
287             trueExp = QuoteExp.trueExp;
288             falseExp = QuoteExp.falseExp;
289         } else {
290             trueExp = QuoteExp.getInstance(language.booleanObject(true));
291             falseExp = QuoteExp.getInstance(language.booleanObject(false));
292         }
293         IfExp.compile(arg, falseExp, trueExp, comp, target);
294         return true;
295     }
296 
compileEq(ApplyExp exp, Compilation comp, Target target, Procedure proc)297     public static boolean compileEq(ApplyExp exp, Compilation comp,
298                                     Target target, Procedure proc) {
299         if (! exp.isSimple())
300             return false;
301         Expression[] args = exp.getArgs();
302         Language language = ((IsEq) proc).language;
303         CodeAttr code = comp.getCode();
304         Expression arg0 = args[0];
305         Expression arg1 = args[1];
306         if (arg0==QuoteExp.nullExp) {
307             Expression tmp = arg1; arg1 = arg0; arg0 = tmp;
308         }
309         arg0.compile(comp, Target.pushObject);
310         boolean isNull = arg1==QuoteExp.nullExp;
311         if (! isNull)
312             arg1.compile(comp, Target.pushObject);
313 
314         if (target instanceof ConditionalTarget) {
315             ConditionalTarget ctarget = (ConditionalTarget) target;
316             if (ctarget.trueBranchComesFirst) {
317                 if (isNull)
318                     code.emitGotoIfNonNull(ctarget.ifFalse);
319                 else
320                     code.emitGotoIfNE(ctarget.ifFalse);
321             } else {
322                 if (isNull)
323                     code.emitGotoIfNull(ctarget.ifTrue);
324                 else
325                     code.emitGotoIfEq(ctarget.ifTrue);
326             }
327             ctarget.emitGotoFirstBranch(code);
328         }
329     else
330       {
331 	Type type;
332         if (isNull)
333             code.emitIfNull();
334         else {
335             code.emitIfEq();
336         }
337 	if (target.getType() instanceof ClassType)
338 	  {
339 	    Object trueValue = language.booleanObject(true);
340 	    Object falseValue = language.booleanObject(false);
341 	    comp.compileConstant(trueValue, Target.pushObject);
342 	    code.emitElse();
343 	    comp.compileConstant(falseValue, Target.pushObject);
344 	    if (trueValue instanceof Boolean && falseValue instanceof Boolean)
345 	      type = Compilation.scmBooleanType;
346 	    else
347 	      type = Type.pointer_type;
348 	  }
349 	else
350 	  {
351 	    code.emitPushInt(1);
352 	    code.emitElse();
353 	    code.emitPushInt(0);
354 	    type = language.getTypeFor(Boolean.TYPE);
355 	  }
356 	code.emitFi();
357 	target.compileFromStack(comp, type);
358       }
359       return true;
360   }
361 
compileNumberCompare(ApplyExp exp, Compilation comp, Target target, Procedure procedure)362     public static boolean compileNumberCompare(ApplyExp exp, Compilation comp, Target target, Procedure procedure) {
363         NumberCompare proc = (NumberCompare) procedure;
364         if (! exp.isSimple())
365             return false;
366         CodeAttr code = comp.getCode();
367         Expression[] args = exp.getArgs();
368         if (args.length == 2) {
369             Expression arg0 = args[0];
370             Expression arg1 = args[1];
371             int kind0 = classifyForNumCompare(arg0);
372             int kind1 = classifyForNumCompare(arg1);
373             if (kind0 > 0 && kind1 > 0
374                 && kind0 <= Arithmetic.REALNUM_CODE && kind1 <= Arithmetic.REALNUM_CODE
375                 // Don't optimize if both operands are fractions. FIXME???
376                 && (kind0 != Arithmetic.RATNUM_CODE || kind1 != Arithmetic.RATNUM_CODE)) {
377                 if (! (target instanceof ConditionalTarget)) {
378                     IfExp.compile(exp, QuoteExp.trueExp, QuoteExp.falseExp,
379                                   comp, target);
380                     return true;
381                 }
382                 int mask = proc.flags;
383                 if (mask == NumberCompare.TRUE_IF_NEQ)
384                     mask = NumberCompare.TRUE_IF_GRT|NumberCompare.TRUE_IF_LSS;
385                 if (kind0 <= Arithmetic.INTNUM_CODE
386                     && kind1 <= Arithmetic.INTNUM_CODE
387                     && (kind0 > Arithmetic.LONG_CODE
388                         || kind1 > Arithmetic.LONG_CODE)
389                     && ! (kind0 == Arithmetic.ULONG_CODE
390                           && kind1 == Arithmetic.ULONG_CODE)) {
391                     // Use either IntNum.compare(intNum,IntNum)
392                     // or (if possible) IntNum.compare(IntNum,long).
393                     // Note we handle (ulong,int) and (ulong,long) by
394                     // delegating to IntNum.compare.  FIXME
395                     Type[] ctypes = new Type[2];
396                     ctypes[0] = Arithmetic.typeIntNum;
397                     if (kind1 <= Arithmetic.LONG_CODE) {
398                         ctypes[1] = Type.longType;
399                     } else if (kind0 <= Arithmetic.LONG_CODE
400                                // Simple check to avoid re-ordering side-effects.
401                                && (arg0 instanceof QuoteExp
402                                    || arg1 instanceof QuoteExp
403                                    || arg0 instanceof ReferenceExp
404                                    || arg1 instanceof ReferenceExp)) {
405                         ctypes[1] = Type.longType;
406                         args = new Expression[2];
407                         args[0] = arg1;
408                         args[1] = arg0;
409                         if (mask != NumberCompare.TRUE_IF_EQU && mask != NumberCompare.TRUE_IF_GRT+NumberCompare.TRUE_IF_LSS)
410                             mask ^= NumberCompare.TRUE_IF_GRT|NumberCompare.TRUE_IF_LSS;
411                     } else
412                         ctypes[1] = Arithmetic.typeIntNum;
413                     Method cmeth
414                         = Arithmetic.typeIntNum.getMethod("compare", ctypes);
415                     PrimProcedure compare =
416                         new PrimProcedure(cmeth, comp.getLanguage());
417                     arg0 = new ApplyExp(compare, args);
418                     arg1 = new QuoteExp(IntNum.zero());
419                     kind0 = kind1 = Arithmetic.INT_CODE;
420                 }
421                 Type commonType;
422                 if (kind0 <= Arithmetic.INT_CODE && kind1 <= Arithmetic.INT_CODE)
423                     commonType = Type.intType;
424                 else if (kind0 <= Arithmetic.UINT_CODE
425                          && kind1 <= Arithmetic.UINT_CODE) {
426                     if (kind0 <= Arithmetic.INT_CODE
427                         || kind1 <= Arithmetic.INT_CODE) {
428                         // Mix of signed and unsigned int - use long
429                         commonType = Type.longType;
430                         kind0 = kind1 = Arithmetic.LONG_CODE;
431                     } else {
432                         // Both operands are unsigned int.
433                         Expression signBit =
434                             QuoteExp.makeShared(Integer.MIN_VALUE,
435                                                 Type.intType);
436                         arg0 = new ApplyExp(AddOp.PLUS, arg0, signBit);
437                         arg1 = new ApplyExp(AddOp.PLUS, arg1, signBit);
438                         kind0 = kind1 = Arithmetic.INT_CODE;
439                         commonType = Type.intType;
440                     }
441                 } else if (kind0 <= Arithmetic.LONG_CODE
442                            && kind1 <= Arithmetic.LONG_CODE) {
443                     commonType = Type.longType;
444                 } else if (kind0 <= Arithmetic.ULONG_CODE
445                            && kind1 <= Arithmetic.ULONG_CODE) {
446                     // Both operands unsigned long.
447                     // (Mix of unsigned long and signed int/long handled above.)
448                     Expression signBit =
449                         QuoteExp.makeShared(Long.MIN_VALUE, Type.longType);
450                     arg0 = new ApplyExp(AddOp.PLUS, arg0, signBit);
451                     arg1 = new ApplyExp(AddOp.PLUS, arg1, signBit);
452                     kind0 = kind1 = Arithmetic.LONG_CODE;
453                     commonType = Type.longType;
454                 } else
455                     commonType = Type.doubleType;
456                 StackTarget subTarget = new StackTarget(commonType);
457                 ConditionalTarget ctarget = (ConditionalTarget) target;
458 
459                 int opcode;
460                 if (arg0 instanceof QuoteExp && ! (arg1 instanceof QuoteExp)) {
461                     Expression tmp = arg1; arg1 = arg0; arg0 = tmp;
462                     if (mask != NumberCompare.TRUE_IF_EQU && mask !=NumberCompare. TRUE_IF_GRT+NumberCompare.TRUE_IF_LSS)
463                         mask ^= NumberCompare.TRUE_IF_GRT|NumberCompare.TRUE_IF_LSS;
464                 }
465                 Label label1 = ctarget.trueBranchComesFirst ? ctarget.ifFalse : ctarget.ifTrue;
466                 if (ctarget.trueBranchComesFirst)
467                     mask ^= NumberCompare.TRUE_IF_GRT|NumberCompare.TRUE_IF_LSS|NumberCompare.TRUE_IF_EQU;
468                 switch (mask) {
469                 case NumberCompare.TRUE_IF_GRT:
470                     opcode = 157 /*ifgt*/;  break;
471                 case NumberCompare.TRUE_IF_EQU:
472                     opcode = 153 /*ifeq*/;  break;
473                 case NumberCompare.TRUE_IF_LSS:
474                     opcode = 155 /*iflt*/;  break;
475                 case NumberCompare.TRUE_IF_GRT|NumberCompare.TRUE_IF_LSS:
476                     opcode = 154 /*ifne*/;  break;
477                 case NumberCompare.TRUE_IF_GRT|NumberCompare.TRUE_IF_EQU:
478                     opcode = 156 /*ifge*/;  break;
479                 case NumberCompare.TRUE_IF_LSS|NumberCompare.TRUE_IF_EQU:
480                     opcode = 158 /*ifle*/;  break;
481                 default:
482                     opcode = 0;
483                 }
484                 arg0.compile(comp, subTarget);
485                 Object value;
486                 if (kind0 <= Arithmetic.INT_CODE && kind1 <= Arithmetic.INT_CODE
487                     && arg1 instanceof QuoteExp
488                     && (value = ((QuoteExp) arg1).getValue()) instanceof IntNum
489                     && ((IntNum) value).isZero()) {
490                     code.emitGotoIfCompare1(label1, opcode);
491                 } else {
492                     arg1.compile(comp, subTarget);
493                     code.emitGotoIfCompare2(label1, opcode);
494                 }
495                 ctarget.emitGotoFirstBranch(code);
496                 return true;
497             }
498         }
499 
500         String mname;
501         switch (proc.flags) {
502         case NumberCompare.TRUE_IF_GRT:
503             mname = "$Gr";  break;
504         case NumberCompare.TRUE_IF_EQU:
505             mname = "$Eq";  break;
506         case NumberCompare.TRUE_IF_LSS:
507             mname = "$Ls";  break;
508         case NumberCompare.TRUE_IF_GRT|NumberCompare.TRUE_IF_EQU:
509             mname = "$Gr$Eq";  break;
510         case NumberCompare.TRUE_IF_LSS|NumberCompare.TRUE_IF_EQU:
511             mname = "$Ls$Eq";  break;
512         default:
513             mname = null;
514         }
515         if (mname != null) {
516             ClassType compclass =
517                 ClassType.make("gnu.kawa.functions.NumberCompare");
518             Method meth = args.length == 2
519                 ? compclass.getDeclaredMethod(mname, 2)
520                 : compclass.getDeclaredMethod(mname+"$V", 4);
521             new ApplyExp(meth, args).setLine(exp)
522                 .compile(comp, target);
523             return true;
524         }
525         return false;
526     }
527 
classifyForNumCompare(Expression exp)528   static int classifyForNumCompare (Expression exp)
529   {
530     Type type = exp.getType();
531     int kind = Arithmetic.classifyType(type);
532     Object value;
533     if (kind == Arithmetic.INTNUM_CODE && exp instanceof QuoteExp
534 	&& (value = ((QuoteExp) exp).getValue()) instanceof IntNum)
535       {
536 	int ilength = ((IntNum) value).intLength();
537 	if (ilength < 32)
538 	  return Arithmetic.INT_CODE;
539 	if (ilength < 64)
540 	  return Arithmetic.LONG_CODE;
541       }
542     return kind;
543   }
544 
compileNumPredicate(ApplyExp exp, Compilation comp, Target target, Procedure procedure)545     public static boolean compileNumPredicate(ApplyExp exp, Compilation comp,
546                                               Target target,
547                                               Procedure procedure) {
548         NumberPredicate proc = (NumberPredicate) procedure;
549         if (! exp.isSimple())
550             return false;
551         Expression[] args = exp.getArgs();
552         int op = proc.op;
553         if (args.length == 1
554             && (op == NumberPredicate.ODD || op == NumberPredicate.EVEN)) {
555             Expression arg0 = args[0];
556             int kind = Arithmetic.classifyType(arg0.getType());
557             CodeAttr code = comp.getCode();
558             if (kind <= Arithmetic.INTNUM_CODE) {
559                 PrimType wtype = Type.intType;
560                 Target wtarget = StackTarget.getInstance(wtype);
561                 if (op == NumberPredicate.EVEN)
562                     code.emitPushInt(1);
563                 arg0.compile(comp, wtarget);
564                 code.emitPushInt(1);
565                 code.emitAnd();
566                 if (op == NumberPredicate.EVEN)
567                     code.emitSub(Type.intType);
568             } else {
569                 arg0.compile(comp, Target.pushObject);
570                 String mname = op == NumberPredicate.EVEN ? "isEven" : "isOdd";
571                 Method m = ClassType
572                     .make("gnu.kawa.functions.NumberPredicate")
573                     .getDeclaredMethod(mname, 1);
574                 code.emitInvokeStatic(m);
575             }
576             target.compileFromStack(comp, Type.booleanType);
577             return true;
578         }
579         return false;
580     }
581 
validateApplyCallCC(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)582   public static Expression validateApplyCallCC
583   (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
584   {
585     LambdaExp lexp = canInlineCallCC(exp);
586     if (lexp != null)
587       {
588         lexp.setInlineOnly(exp, visitor.getCurrentLambda());
589         Declaration contDecl = lexp.firstDecl();
590         if (! contDecl.getFlag(Declaration.TYPE_SPECIFIED))
591           contDecl.setType(typeContinuation);
592         contDecl.setFlag(Declaration.DONT_COPY);
593         LambdaExp.maybeSetReturnType(lexp, required);
594         // FIXME (future): (after visiting lexp), do:
595         // exp.setType(lexp.body.getType() UNION continuation-arguments);
596       }
597     exp.visitArgs(visitor);
598     return exp;
599   }
600 
601   public static final ClassType typeContinuation =
602     ClassType.make("kawa.lang.Continuation");
603 
compileCallCC(ApplyExp exp, Compilation comp, Target target, Procedure proc)604   public static void compileCallCC (ApplyExp exp, Compilation comp, Target target, Procedure proc)
605   {
606     LambdaExp lambda = canInlineCallCC(exp);
607     if (lambda == null)
608       {
609 	ApplyExp.compile(exp, comp, target);
610 	return;
611       }
612     CodeAttr code = comp.getCode();
613     final Declaration param = lambda.firstDecl();
614     if (param.isSimple() && ! param.getCanRead() && ! param.getCanWrite())
615       {
616         param.setCanCall(false);
617         CompileTimeContinuation contProxy = new CompileTimeContinuation();
618         Type rtype = target instanceof StackTarget ? target.getType() : null;
619         boolean runFinallyBlocks
620           = ExitThroughFinallyChecker.check(param, lambda.body);
621         ExitableBlock bl = code.startExitableBlock(rtype, runFinallyBlocks);
622         contProxy.exitableBlock = bl;
623         contProxy.blockTarget = target;
624         param.setValue(new QuoteExp(contProxy));
625         (new ApplyExp(lambda, QuoteExp.nullExp)).compile(comp, target);
626         code.endExitableBlock();
627         return;
628       }
629 
630     Scope sc = code.pushScope();
631     Variable contVar = sc.addVariable(code, typeContinuation, null);
632     Declaration contDecl = new Declaration(contVar);
633     code.emitNew(typeContinuation);
634     code.emitDup(typeContinuation);
635     comp.loadCallContext();
636     code.emitInvokeSpecial(typeContinuation.getDeclaredMethod("<init>", 1));
637     code.emitStore(contVar);
638     code.emitTryStart(false, target instanceof IgnoreTarget || target instanceof ConsumerTarget ? null : target.getType().getRawType());
639     ApplyExp app = new ApplyExp(lambda, new ReferenceExp(contDecl) );
640     app.compile(comp, target);
641     // Emit: cont.invoked = true
642     if (code.reachableHere())
643       {
644         code.emitLoad(contVar);
645         code.emitPushInt(1);
646         code.emitPutField(typeContinuation.getField("invoked"));
647       }
648 
649     // Emit: catch (Throwable ex) { handleException$(ex, cont, ctx); }
650     code.emitCatchStart((ClassType) null);
651     code.emitLoad(contVar);
652     if (target instanceof ConsumerTarget)
653       {
654         comp.loadCallContext();
655         Method handleMethod = typeContinuation.getDeclaredMethod("handleException$X", 3);
656         code.emitInvokeStatic(handleMethod);
657       }
658     else
659       {
660         Method handleMethod = typeContinuation.getDeclaredMethod("handleException", 2);
661         code.emitInvokeStatic(handleMethod);
662         target.compileFromStack(comp, Type.objectType);
663       }
664     code.emitCatchEnd();
665 
666     code.emitTryCatchEnd();
667     code.popScope();
668   }
669 
670   /** If we can inline, return LambdaExp for first arg; otherwise null. */
canInlineCallCC(ApplyExp exp)671   private static LambdaExp canInlineCallCC (ApplyExp exp)
672   {
673     Expression[] args = exp.getArgs();
674     Expression arg0;
675     if (args.length == 1 && (arg0 = args[0]) instanceof LambdaExp
676         && ! Compilation.enableANF)
677       {
678         LambdaExp lexp = (LambdaExp) arg0;
679         if (lexp.min_args == 1 && lexp.max_args == 1
680             && ! lexp.firstDecl().getCanWrite())
681           {
682             return lexp;
683           }
684       }
685     return null;
686   }
687 
validateApplyWithExceptionHandler(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)688     public static Expression validateApplyWithExceptionHandler
689         (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc) {
690         Expression[] args = exp.getArgs();
691         Expression handler = visitor.visit(args[0], null);
692         args[0] = handler;
693         Expression thunk = args[1];
694         if (thunk instanceof LambdaExp) {
695             LambdaExp lthunk = (LambdaExp) thunk;
696             if (lthunk.min_args == 0 && lthunk.max_args == 0) {
697                 LambdaExp.maybeSetReturnType(lthunk, required);
698                 thunk = visitor.visit(lthunk, null);
699                 args[1] = thunk;
700 
701                 /* Rewrite to:
702                      (let ((link (HandlerLink:push handler)))
703                       (try-catch
704                               (let ((v (thunk)))
705                                (link:pop)
706                                v)
707                        (ex java.lang.Throwable
708                         (primitive-throw (link:handle ex)))))
709                 * This allows the thunk to be inlined.
710                 */
711                 Compilation comp = visitor.getCompilation();
712                 comp.letStart();
713                 ClassType handlerLinkType =
714                     ClassType.make("kawa.lib.HandlerLink");
715                 Method pushMethod =
716                     handlerLinkType.getDeclaredMethod("push", 1);
717                 Method popMethod =
718                     handlerLinkType.getDeclaredMethod("pop", 0);
719                 Declaration linkDecl =
720                     comp.letVariable(null, handlerLinkType,
721                                      new ApplyExp(pushMethod, handler));
722                 comp.letEnter();
723                 Expression tryClause;
724                 Type bodyType = lthunk.getReturnType();
725                 Expression bodyCall = new ApplyExp(thunk);
726                 Expression popHandler =
727                     new ApplyExp(popMethod, new ReferenceExp(linkDecl));
728                 if (bodyType.isVoid()) {
729                     tryClause = new BeginExp(bodyCall, popHandler);
730                 } else {
731                     comp.letStart();
732                     Declaration resultDecl =
733                         comp.letVariable(null, bodyType, bodyCall);
734                     comp.letEnter();
735                     tryClause =
736                         comp.letDone
737                         (new BeginExp(popHandler,
738                                       new ReferenceExp(resultDecl)));
739                 }
740                 TryExp texp = new TryExp(tryClause, null);
741                 Declaration exDecl =
742                     new Declaration(null, Type.javalangThrowableType);
743 
744                 Expression doHandle =
745                     new ApplyExp(handlerLinkType
746                                  .getDeclaredMethod("handle", 1),
747                                  new ReferenceExp(linkDecl),
748                                  new ReferenceExp(exDecl));
749                 texp.addCatchClause(exDecl,
750                                     new ApplyExp(Throw.primitiveThrow,
751                                                  doHandle));
752                 return visitor.visit(comp.letDone(texp), required);
753             }
754         }
755         thunk = visitor.visit(thunk, null);
756         args[1] = thunk;
757         return exp;
758     }
759 
validateApplyMakeDynamic(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)760     public static Expression validateApplyMakeDynamic
761         (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc) {
762         if (exp.isSimple(1, 1)) {
763             Expression[] args = exp.getArgs();
764             args[0] = visitor.visit(args[0], null);
765             exp.setType(LangObjType.dynamicType);
766         } else
767             exp.visitArgs(visitor);
768         return exp;
769     }
770 
compileMakeDynamic(ApplyExp exp, Compilation comp, Target target, Procedure procedure)771     public static boolean compileMakeDynamic(ApplyExp exp, Compilation comp,
772                                              Target target,
773                                              Procedure procedure) {
774         if (! exp.isSimple())
775             return false;
776         exp.getArg(0).compile(comp, target);
777         return true;
778     }
779 
780   /** An ExpVisitor class to check if callcc exits through a try-finally. */
781   static class ExitThroughFinallyChecker extends ExpVisitor<Expression,TryExp>
782   {
783     Declaration decl;
784 
785     /** Does decl appear in body nested inside a try-finally? */
check(Declaration decl, Expression body)786     public static boolean check (Declaration decl, Expression body)
787     {
788       ExitThroughFinallyChecker visitor = new ExitThroughFinallyChecker();
789       visitor.decl = decl;
790       visitor.visit(body, null);
791       return visitor.exitValue != null;
792     }
793 
defaultValue(Expression r, TryExp d)794     protected Expression defaultValue(Expression r, TryExp d)
795     {
796       return r;
797     }
798 
visitReferenceExp(ReferenceExp exp, TryExp currentTry)799     protected Expression visitReferenceExp (ReferenceExp exp, TryExp currentTry)
800     {
801       if (decl == exp.getBinding() && currentTry != null)
802         exitValue = Boolean.TRUE;
803       return exp;
804     }
805 
visitTryExp(TryExp exp, TryExp currentTry)806     protected Expression visitTryExp (TryExp exp, TryExp currentTry)
807     {
808       visitExpression(exp, exp.getFinallyClause() != null ? exp : currentTry);
809       return exp;
810     }
811   }
812 
validateApplyMakePromise(ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)813   public static Expression validateApplyMakePromise
814   (ApplyExp exp, InlineCalls visitor, Type required, Procedure proc)
815   {
816     Expression[] args = exp.getArgs();
817     if (args.length == 1 && args[0] instanceof LambdaExp)
818       {
819         boolean forceValueIfPromise = proc == MakePromise.makeLazy;
820         Type bodyRequired;
821         if (required instanceof LazyType)
822           {
823             Type valueType = ((LazyType) required).getValueType();
824             bodyRequired = forceValueIfPromise ? LazyType.getLazyType(valueType)
825                 : valueType;
826           }
827         else
828           bodyRequired = null;
829         LambdaExp lexp = (LambdaExp) args[0];
830         lexp.body = visitor.visit(lexp.body, bodyRequired);
831         args[0] = visitor.visit(lexp, null);
832         Type rtype = lexp.getReturnType();
833         if (forceValueIfPromise)
834           {
835             rtype = rtype instanceof LazyType
836                 ? ((LazyType) rtype).getValueType()
837                 : Type.objectType;
838           }
839         Type type = LazyType.getLazyType(rtype);
840         String mname = forceValueIfPromise ? "makePromiseLazy" : "makePromise";
841         Method meth = ClassType.make("gnu.kawa.functions.MakePromise")
842             .getDeclaredMethod(mname, 1);
843 	PrimProcedure mproc
844 	  = new PrimProcedure(meth);
845         mproc.setReturnType(type);
846         exp = new ApplyExp(mproc, args);
847         exp.setType(type);
848       }
849     else
850       {
851         exp.visitArgs(visitor);
852       }
853     return exp;
854   }
855 
856     /** A hack to simplify inlining continuation calls.
857      */
858     static class CompileTimeContinuation extends ProcedureN implements Inlineable {
859         Target blockTarget;
860         ExitableBlock exitableBlock;
861 
applyN(Object[] args)862         public Object applyN (Object[] args) throws Throwable {
863             throw new Error("internal error");
864         }
865 
compile(ApplyExp exp, Compilation comp, Target target)866         public void compile (ApplyExp exp, Compilation comp, Target target) {
867             CodeAttr code = comp.getCode();
868             Expression[] args = exp.getArgs();
869             int nargs = args.length;
870             boolean noStack = (blockTarget instanceof IgnoreTarget
871                                || blockTarget instanceof ConsumerTarget);
872             Type typeNeeded = noStack ? null : target.getType();
873             if (noStack || nargs == 1) {
874                 for (int i = 0;  i < nargs;  i++)
875                     args[i].compileWithPosition(comp, blockTarget);
876             } else {
877                 AppendValues app = AppendValues.appendValues;
878                 app.compile(new ApplyExp(app, args), comp, blockTarget);
879             }
880             exitableBlock.exit();
881         }
882 
getReturnType(Expression[] args)883         public gnu.bytecode.Type getReturnType (Expression[] args) {
884             return Type.neverReturnsType;
885         }
886     }
887 }
888