static void compile(ApplyExp exp, Compilation comp, Target target, boolean checkInlineable) { int args_length = exp.args.length; Expression exp_func = exp.func; LambdaExp func_lambda = null; String func_name = null; Declaration owner = null; Object quotedValue = null; if (exp_func instanceof LambdaExp) { func_lambda = (LambdaExp) exp_func; func_name = func_lambda.getName(); if (func_name == null) func_name = "<lambda>"; } else if (exp_func instanceof ReferenceExp) { ReferenceExp func_ref = (ReferenceExp) exp_func; owner = func_ref.contextDecl(); Declaration func_decl = func_ref.binding; while (func_decl != null && func_decl.isAlias() && func_decl.value instanceof ReferenceExp) { func_ref = (ReferenceExp) func_decl.value; if (owner != null || func_decl.needsContext() || func_ref.binding == null) break; func_decl = func_ref.binding; owner = func_ref.contextDecl(); } if (!func_decl.getFlag(Declaration.IS_UNKNOWN)) { Expression value = func_decl.getValue(); func_name = func_decl.getName(); if (value != null && value instanceof LambdaExp) func_lambda = (LambdaExp) value; if (value != null && value instanceof QuoteExp) quotedValue = ((QuoteExp) value).getValue(); } } else if (exp_func instanceof QuoteExp) { quotedValue = ((QuoteExp) exp_func).getValue(); } if (checkInlineable && quotedValue instanceof Procedure) { Procedure proc = (Procedure) quotedValue; if (target instanceof IgnoreTarget && proc.isSideEffectFree()) { for (int i = 0; i < args_length; i++) exp.args[i].compile(comp, target); return; } try { if (inlineCompile(proc, exp, comp, target)) return; } catch (Throwable ex) { comp.getMessages() .error('e', "caught exception in inline-compiler for " + quotedValue + " - " + ex, ex); return; } } gnu.bytecode.CodeAttr code = comp.getCode(); Method method; if (func_lambda != null) { if ((func_lambda.max_args >= 0 && args_length > func_lambda.max_args) || args_length < func_lambda.min_args) // This is supposed to get caught by InlineCalls. throw new Error("internal error - wrong number of parameters for " + func_lambda); int conv = func_lambda.getCallConvention(); // Mostly duplicates logic with LambdaExp.validateApply. // See comment there. if (comp.inlineOk(func_lambda) && (conv <= Compilation.CALL_WITH_CONSUMER || (conv == Compilation.CALL_WITH_TAILCALLS && !exp.isTailCall())) && (method = func_lambda.getMethod(args_length)) != null) { PrimProcedure pproc = new PrimProcedure(method, func_lambda); boolean is_static = method.getStaticFlag(); boolean extraArg = false; // ?? Procedure.checkArgCount(this, args.length); // FIXME if (!is_static || func_lambda.declareClosureEnv() != null) { if (is_static) extraArg = true; if (comp.curLambda == func_lambda) // Recursive call. code.emitLoad( func_lambda.closureEnv != null ? func_lambda.closureEnv : func_lambda.thisVariable); else if (owner != null) owner.load(null, 0, comp, Target.pushObject); else func_lambda.getOwningLambda().loadHeapFrame(comp); } pproc.compile(extraArg ? Type.voidType : null, exp, comp, target); return; } } /* if (comp.usingCPStyle()) { { Label l = new Label(code); gnu.bytecode.SwitchState fswitch = comp.fswitch; int pc = fswitch.getMaxValue() + 1; fswitch.addCase(pc, code, l); exp_func.compile(comp, new StackTarget(Compilation.typeProcedure)); comp.loadCallContext(); // Emit: context->pc = pc. comp.loadCallContext(); code.emitPushInt(pc); code.emitPutField(Compilation.pcCallContextField); code.emitInvokeVirtual(Compilation.applyCpsMethod); // emit[save java stack, if needed] Type[] stackTypes = code.saveStackTypeState(false); java.util.Stack stackFields = new java.util.Stack(); if (stackTypes != null) { for (int i = stackTypes.length; --i >= 0; ) { Field fld = comp.allocLocalField (stackTypes[i], null); code.emitPushThis(); code.emitSwap(); code.emitPutField(fld); stackFields.push(fld); } } code.emitReturn(); l.define(code); // emit[restore java stack, if needed] if (stackTypes != null) { for (int i = stackTypes.length; --i >= 0; ) { Field fld = (Field) stackFields.pop(); code.emitPushThis(); code.emitGetField(fld); comp.freeLocalField(fld); } } // FIXME // Load result from stack.value to target. comp.loadCallContext(); code.emitGetField(comp.valueCallContextField); target.compileFromStack(comp, Type.pointer_type); } return; } */ // Check for tail-recursion. boolean tail_recurse = exp.isTailCall() && func_lambda != null && func_lambda == comp.curLambda; if (func_lambda != null && func_lambda.getInlineOnly() && !tail_recurse && func_lambda.min_args == args_length) { pushArgs(func_lambda, exp.args, null, comp); if (func_lambda.getFlag(LambdaExp.METHODS_COMPILED)) { popParams(code, func_lambda, null, false); code.emitTailCall(false, func_lambda.getVarScope()); return; } func_lambda.flags |= LambdaExp.METHODS_COMPILED; LambdaExp saveLambda = comp.curLambda; comp.curLambda = func_lambda; func_lambda.allocChildClasses(comp); func_lambda.allocParameters(comp); popParams(code, func_lambda, null, false); func_lambda.enterFunction(comp); func_lambda.body.compileWithPosition(comp, target); func_lambda.compileEnd(comp); func_lambda.generateApplyMethods(comp); code.popScope(); comp.curLambda = saveLambda; return; } if (comp.curLambda.isHandlingTailCalls() && (exp.isTailCall() || target instanceof ConsumerTarget) && !comp.curLambda.getInlineOnly()) { ClassType typeContext = Compilation.typeCallContext; exp_func.compile(comp, new StackTarget(Compilation.typeProcedure)); // evaluate args to frame-locals vars; // may recurse! if (args_length <= 4) { for (int i = 0; i < args_length; ++i) exp.args[i].compileWithPosition(comp, Target.pushObject); comp.loadCallContext(); code.emitInvoke( Compilation.typeProcedure.getDeclaredMethod("check" + args_length, args_length + 1)); } else { compileToArray(exp.args, comp); comp.loadCallContext(); code.emitInvoke(Compilation.typeProcedure.getDeclaredMethod("checkN", 2)); } if (exp.isTailCall()) { code.emitReturn(); } else if (((ConsumerTarget) target).isContextTarget()) { comp.loadCallContext(); code.emitInvoke(typeContext.getDeclaredMethod("runUntilDone", 0)); } else { comp.loadCallContext(); code.emitLoad(((ConsumerTarget) target).getConsumerVariable()); code.emitInvoke(typeContext.getDeclaredMethod("runUntilValue", 1)); } return; } if (!tail_recurse) exp_func.compile(comp, new StackTarget(Compilation.typeProcedure)); boolean toArray = (tail_recurse ? func_lambda.min_args != func_lambda.max_args : args_length > 4); int[] incValues = null; // Increments if we can use iinc. if (toArray) { compileToArray(exp.args, comp); method = Compilation.applyNmethod; } else if (tail_recurse) { incValues = new int[exp.args.length]; pushArgs(func_lambda, exp.args, incValues, comp); method = null; } else { for (int i = 0; i < args_length; ++i) { exp.args[i].compileWithPosition(comp, Target.pushObject); if (!code.reachableHere()) break; } method = Compilation.applymethods[args_length]; } if (!code.reachableHere()) { comp.error('e', "unreachable code"); return; } if (tail_recurse) { popParams(code, func_lambda, incValues, toArray); code.emitTailCall(false, func_lambda.getVarScope()); return; } code.emitInvokeVirtual(method); target.compileFromStack(comp, Type.pointer_type); }
static { typeProcessingInstructionType = ClassType.make("gnu.kawa.xml.ProcessingInstructionType"); coerceMethod = typeProcessingInstructionType.getDeclaredMethod("coerce", 2); coerceOrNullMethod = typeProcessingInstructionType.getDeclaredMethod("coerceOrNull", 2); }
/** A type that matches some number of repetitions of a basetype. */ public class OccurrenceType extends ObjectType implements Externalizable, TypeValue { Type base; int minOccurs; int maxOccurs; public Type getBase() { return base; } public int minOccurs() { return minOccurs; } public int maxOccurs() { return maxOccurs; } public OccurrenceType(Type base, int minOccurs, int maxOccurs) { this.base = base; this.minOccurs = minOccurs; this.maxOccurs = maxOccurs; } public static Type getInstance(Type base, int minOccurs, int maxOccurs) { if (minOccurs == 1 && maxOccurs == 1) return base; if (minOccurs == 0 && maxOccurs < 0 && (base == SingletonType.instance || base == Type.pointer_type)) return Type.pointer_type; return new OccurrenceType(base, minOccurs, maxOccurs); } public static final Type emptySequenceType = OccurrenceType.getInstance(SingletonType.instance, 0, 0); public Type getImplementationType() { return Type.pointer_type; } public int compare(Type other) { if (other instanceof OccurrenceType) { OccurrenceType occOther = (OccurrenceType) other; if (minOccurs == occOther.minOccurs && maxOccurs == occOther.maxOccurs) return base.compare(occOther.getBase()); } /* Type primeThis = itemPrimeType(getBase()); Type primeOther = itemPrimeType(other); FIXME: Compare primThis with primOther AND the occurrence numbers. */ return -2; } public Object coerceFromObject(Object obj) { if (obj instanceof Values) { } else { // Assumes that base is an item type. FIXME. if (minOccurs <= 1 && maxOccurs != 0) return base.coerceFromObject(obj); } // FIXME if (!isInstance(obj)) throw new ClassCastException(); return obj; } public boolean isInstance(Object obj) { if (obj instanceof Values) { Values vals = (Values) obj; int pos = vals.startPos(); int n = 0; if (base instanceof ItemPredicate) { ItemPredicate pred = (ItemPredicate) base; for (; ; ) { boolean matches; matches = pred.isInstancePos(vals, pos); pos = vals.nextPos(pos); if (pos == 0) { return n >= minOccurs && (maxOccurs < 0 || n <= maxOccurs); } if (!matches) return false; n++; } } else { for (; ; ) { pos = vals.nextPos(pos); if (pos == 0) { return n >= minOccurs && (maxOccurs < 0 || n <= maxOccurs); } Object value = vals.getPosPrevious(pos); if (!base.isInstance(value)) return false; n++; } } } else { if (minOccurs > 1 || maxOccurs == 0) return false; return base.isInstance(obj); } } public void emitTestIf(Variable incoming, Declaration decl, Compilation comp) { CodeAttr code = comp.getCode(); if (incoming != null) code.emitLoad(incoming); if (decl != null) { code.emitDup(); decl.compileStore(comp); } comp.compileConstant(this); code.emitSwap(); code.emitInvokeVirtual(isInstanceMethod); code.emitIfIntNotZero(); } public void emitIsInstance(Variable incoming, Compilation comp, Target target) { gnu.kawa.reflect.InstanceOf.emitIsInstance(this, incoming, comp, target); } public Expression convertValue(Expression value) { return null; } public Procedure getConstructor() { return null; } /** * Return a conservative estimage on the min/max number of items of a type. * * @return {@code maxCount << 12 | minCount & 0xFFF}, where a {@code maxCount} of -1 means * unbounded. */ public static int itemCountRange(Type type) { if (type instanceof SingletonType) return (1 << 12) | 1; if (type instanceof OccurrenceType) { OccurrenceType occ = (OccurrenceType) type; int min = occ.minOccurs(); int max = occ.maxOccurs(); int bnum = itemCountRange(occ.getBase()); if ((min == 1 && max == 1) || bnum == 0) return bnum; if (max > 0xfffff) max = -1; if (max == 0) return 0; int bmin = bnum & 0xfff; int bmax = bnum >> 12; if (bnum != 0x1001) { if (min > 0xfff) min = 0xfff; min = min * bmin; if (min > 0xfff) min = 0xfff; if (max < 0 || bmax < 0) max = -1; else max = max * bmax; if (max > 0xfffff) max = -1; } return (max << 12) | min; } if (type instanceof PrimType) return type.isVoid() ? 0 : 0x1001; if (type instanceof ArrayType) return 0x1001; if (type instanceof ObjectType) { int cmp = type.compare(Compilation.typeValues); if (cmp == -3) return 0x1001; } return -1 << 12; } /** * Returna a quantifer kind for a sequence type. * * @return '0' if type is known to be a void (0-item) type; '1' if type is known to be a * single-item type; '?' if type matches a sequence of 0 or 1 items; '+' if type matches a * sequence of 1 or more items; '*' otherwise. */ public static char itemCountCode(Type type) { int num = itemCountRange(type); int min = num & 0xFFF; int max = num >> 12; return max == 0 ? '0' : min == 0 ? (max == 1 ? '?' : '*') : min == 1 && max == 1 ? '1' : '+'; } public static boolean itemCountIsZeroOrOne(Type type) { // cute hack for: max == 0 || max == 1. return (itemCountRange(type) >> 13) == 0; } public static boolean itemCountIsOne(Type type) { return itemCountRange(type) == 0x1001; } /** QUery formal semantics "prime type" */ public static Type itemPrimeType(Type type) { while (type instanceof OccurrenceType) type = ((OccurrenceType) type).getBase(); return itemCountIsOne(type) ? type : SingletonType.instance; } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(base); out.writeInt(minOccurs); out.writeInt(maxOccurs); } public String toString() { String b = base.toString(); boolean parens = b == null || b.indexOf(' ') >= 0; StringBuffer sbuf = new StringBuffer(); if (parens) sbuf.append('('); sbuf.append(b); if (parens) sbuf.append(')'); if (minOccurs == 1 && maxOccurs == 1) ; else if (minOccurs == 0 && maxOccurs == 1) sbuf.append('?'); else if (minOccurs == 1 && maxOccurs == -1) sbuf.append('+'); else if (minOccurs == 0 && maxOccurs == -1) sbuf.append('*'); else { sbuf.append('{'); sbuf.append(minOccurs); sbuf.append(','); if (maxOccurs >= 0) sbuf.append(maxOccurs); else sbuf.append('*'); sbuf.append('}'); } return sbuf.toString(); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { base = (Type) in.readObject(); minOccurs = in.readInt(); maxOccurs = in.readInt(); } public static final ClassType typeOccurrenceType = ClassType.make("gnu.kawa.reflect.OccurrenceType"); static final Method isInstanceMethod = typeOccurrenceType.getDeclaredMethod("isInstance", 1); }