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