001: // Copyright (c) 1999, 2000 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.expr;
005:
006: import gnu.bytecode.*;
007: import java.util.Hashtable;
008: import gnu.mapping.*;
009:
010: /** A primitive Procedure implemented by a plain Java method. */
011:
012: public class PrimProcedure extends MethodProc implements
013: gnu.expr.Inlineable {
014: Type retType;
015: Type[] argTypes;
016: Method method;
017: int op_code;
018:
019: /** If non-null, the LambdaExp that this PrimProcedure implements. */
020: LambdaExp source;
021:
022: java.lang.reflect.Member member;
023:
024: public final int opcode() {
025: return op_code;
026: }
027:
028: public Type getReturnType() {
029: return retType;
030: }
031:
032: public void setReturnType(Type retType) {
033: this .retType = retType;
034: }
035:
036: public Type getReturnType(Expression[] args) {
037: return retType;
038: }
039:
040: /** Return true iff the last parameter is a "rest" argument. */
041: public boolean takesVarArgs() {
042: return method != null && method.getName().endsWith("$V");
043: }
044:
045: public int numArgs() {
046: int num = argTypes.length;
047: if (!getStaticFlag())
048: num++;
049: return takesVarArgs() ? (num - 1) + (-1 << 12) : num
050: + (num << 12);
051: }
052:
053: public int match(CallContext ctx, Object[] args) {
054: ctx.setArgsN(args);
055:
056: int nargs = ctx.count;
057: boolean takesVarArgs = takesVarArgs();
058: int mlength = minArgs() + (takesVarArgs ? 1 : 0);
059: int fixArgs = takesVarArgs ? mlength - 1 : mlength;
060:
061: if (takesVarArgs) {
062: if (nargs < fixArgs)
063: return NO_MATCH_TOO_FEW_ARGS | fixArgs;
064: } else {
065: if (nargs != mlength)
066: if (nargs < mlength)
067: return NO_MATCH_TOO_FEW_ARGS | fixArgs;
068: else
069: return NO_MATCH_TOO_MANY_ARGS | fixArgs;
070: }
071: int arg_count = argTypes.length;
072: Type elementType = null;
073: Object[] restArray = null;
074: int this _count = getStaticFlag() ? 0 : 1;
075: Object[] rargs = new Object[mlength - this _count];
076: Object this Value;
077: if (takesVarArgs) {
078: Type restType = argTypes[arg_count - 1];
079: if (restType == Compilation.scmListType) { // FIXME
080: rargs[rargs.length - 1] = gnu.lists.LList.makeList(
081: args, fixArgs);
082: nargs = fixArgs;
083: } else {
084: ArrayType restArrayType = (ArrayType) restType;
085: elementType = restArrayType.getComponentType();
086: Class elementClass = elementType.getReflectClass();
087: restArray = (Object[]) java.lang.reflect.Array
088: .newInstance(elementClass, nargs - fixArgs);
089: rargs[rargs.length - 1] = restArray;
090: }
091: }
092: if (this _count != 0)
093: this Value = method.getDeclaringClass().coerceFromObject(
094: ctx.getArgAsObject(0));
095: else
096: this Value = null;
097: for (int i = this _count; i < nargs; i++) {
098: try {
099: Object arg = ctx.getArgAsObject(i);
100: Type type = i < fixArgs ? argTypes[i - this _count]
101: : elementType;
102: if (type != Type.pointer_type)
103: arg = type.coerceFromObject(arg);
104: if (i < fixArgs)
105: rargs[i - this _count] = arg;
106: else
107: restArray[i - fixArgs] = arg;
108: } catch (ClassCastException ex) {
109: return NO_MATCH_BAD_TYPE | i;
110: }
111: }
112: ctx.value1 = this Value;
113: ctx.value2 = rargs;
114: return 0;
115: }
116:
117: public Object applyV(CallContext ctx) throws Throwable {
118: int arg_count = argTypes.length;
119: boolean is_constructor = op_code == 183;
120: boolean is_static = getStaticFlag();
121:
122: try {
123: if (member == null) {
124: Class clas = method.getDeclaringClass()
125: .getReflectClass();
126: Class[] paramTypes = new Class[arg_count];
127: for (int i = arg_count; --i >= 0;)
128: paramTypes[i] = argTypes[i].getReflectClass();
129: if (is_constructor)
130: member = clas.getConstructor(paramTypes);
131: else
132: member = clas.getMethod(method.getName(),
133: paramTypes);
134: }
135: Object[] rargs = (Object[]) ctx.value2;
136: if (is_constructor)
137: return ((java.lang.reflect.Constructor) member)
138: .newInstance(rargs);
139: else {
140: java.lang.reflect.Method meth = (java.lang.reflect.Method) member;
141: Object result = meth.invoke(ctx.value1, rargs);
142: return retType.coerceToObject(result);
143: }
144: } catch (java.lang.reflect.InvocationTargetException ex) {
145: throw ex.getTargetException();
146: }
147: }
148:
149: public PrimProcedure(java.lang.reflect.Method method,
150: Class this Class, Class[] parameterClasses,
151: Interpreter interpreter) {
152: Type[] parameterTypes = new Type[parameterClasses.length];
153: Type[] implParameterTypes = new Type[parameterClasses.length];
154: for (int i = parameterClasses.length; --i >= 0;) {
155: Type ptype = interpreter.getTypeFor(parameterClasses[i]);
156: parameterTypes[i] = ptype;
157: implParameterTypes[i] = ptype.getImplementationType();
158: }
159: Type returnType = interpreter
160: .getTypeFor(method.getReturnType());
161: Type implReturnType = returnType.getImplementationType();
162: ClassType this Type = (ClassType) interpreter
163: .getTypeFor(this Class);
164: Method meth = this Type.addMethod(method.getName(), method
165: .getModifiers(), implParameterTypes, implReturnType);
166: init(meth);
167: argTypes = parameterTypes;
168: retType = op_code == 183 ? meth.getDeclaringClass()
169: : returnType;
170: }
171:
172: public PrimProcedure(java.lang.reflect.Method method,
173: Interpreter interpreter) {
174: this (method, method.getDeclaringClass(), method
175: .getParameterTypes(), interpreter);
176: }
177:
178: public PrimProcedure(Method method) {
179: init(method);
180: }
181:
182: public PrimProcedure(Method method,
183: java.util.Stack[] parameterCopies) {
184: init(method);
185: this .parameterCopies = parameterCopies;
186: }
187:
188: private java.util.Stack[] parameterCopies;
189:
190: public PrimProcedure(Method method, Interpreter interpreter) {
191: init(method);
192:
193: // This stuff deals with that a language may have its own mapping
194: // from Java types to language types, for coercions and other reasons.
195: Type[] pTypes = method.getParameterTypes();
196: int nTypes = pTypes.length;
197: argTypes = null;
198: for (int i = nTypes; --i >= 0;) {
199: Type javaType = pTypes[i];
200: Type langType = interpreter.getTypeFor(javaType
201: .getReflectClass());
202: if (javaType != langType) {
203: if (argTypes == null) {
204: argTypes = new Type[nTypes];
205: System.arraycopy(pTypes, 0, argTypes, 0, nTypes);
206: }
207: argTypes[i] = langType;
208: }
209: }
210: if (argTypes == null)
211: argTypes = pTypes;
212: retType = op_code == 183 ? method.getDeclaringClass()
213: : interpreter.getTypeFor(method.getReturnType()
214: .getReflectClass());
215: }
216:
217: private boolean isConstructor() {
218: return "<init>".equals(method.getName());
219: }
220:
221: private void init(Method method) {
222: this .method = method;
223: this .argTypes = method.getParameterTypes();
224: this .retType = method.getReturnType();
225: int flags = method.getModifiers();
226: if ((flags & Access.STATIC) != 0)
227: this .op_code = 184; // invokestatic
228: else {
229: ClassType mclass = method.getDeclaringClass();
230: if ((mclass.getModifiers() & Access.INTERFACE) != 0)
231: this .op_code = 185; // invokeinterface
232: else if ("<init>".equals(method.getName())) {
233: this .op_code = 183; // invokespecial
234: this .retType = mclass;
235: } else
236: this .op_code = 182; // invokevirtual
237: }
238: }
239:
240: public static PrimProcedure specialCall(Method method) {
241: PrimProcedure res = new PrimProcedure(method);
242: res.op_code = 183;
243: return res;
244: }
245:
246: public PrimProcedure(Method method, LambdaExp source) {
247: this (method);
248: this .source = source;
249: }
250:
251: public PrimProcedure(int opcode, Type retType, Type[] argTypes) {
252: this .op_code = opcode;
253: this .retType = retType;
254: this .argTypes = argTypes;
255: }
256:
257: public static PrimProcedure makeBuiltinBinary(int opcode, Type type) {
258: // FIXME - should cache!
259: Type[] args = new Type[2];
260: args[0] = type;
261: args[1] = type;
262: return new PrimProcedure(opcode, type, args);
263: }
264:
265: public PrimProcedure(int op_code, ClassType classtype, String name,
266: Type retType, Type[] argTypes) {
267: this .op_code = op_code;
268: if (op_code == 185) // invokeinterface
269: classtype.access_flags |= Access.INTERFACE;
270:
271: method = classtype.getDeclaredMethod(name, argTypes, retType);
272:
273: if (method == null)
274: method = classtype.addMethod(name,
275: op_code == 184 ? Access.STATIC : 0, argTypes,
276: retType);
277: else if ((op_code == 184) != method.getStaticFlag())
278: throw new Error("Method " + method + " should "
279: + (op_code == 184 ? "" : "not ") + "be static");
280:
281: this .retType = retType;
282: this .argTypes = argTypes;
283: }
284:
285: /** Use to compile new followed by constructor. */
286: public PrimProcedure(ClassType classtype, Type[] argTypes) {
287: this (183, classtype, "<init>", Type.void_type, argTypes);
288: this .retType = classtype;
289: }
290:
291: public final boolean getStaticFlag() {
292: return method == null || method.getStaticFlag()
293: || isConstructor();
294: }
295:
296: public final Type[] getParameterTypes() {
297: return argTypes;
298: }
299:
300: public static void compileArgs(Expression[] args, Type this Type,
301: Type[] argTypes, boolean variable, String name,
302: LambdaExp source, Compilation comp) {
303: compileArgs(args, this Type, argTypes, variable, name, source,
304: comp, source == null ? null : source.parameterCopies);
305: }
306:
307: /** Compile arguments and push unto stack.
308: * @param args arguments to evaluate and push.
309: * @param thisType If we are calling a non-static function,
310: * then args[0] is the receiver and thisType is its expected class.
311: * If thisType==Type.void_type, ignore argTypes[0]. (It is used to to
312: * pass a link to a closure environment, which was pushed by our caller.)
313: * If this_type==null, no special handling of args[0] or argTypes[0].
314: */
315: public static void compileArgs(Expression[] args, Type this Type,
316: Type[] argTypes, boolean variable, String name,
317: LambdaExp source, Compilation comp,
318: java.util.Stack[] parameterCopies) {
319: Type arg_type = null;
320: gnu.bytecode.CodeAttr code = comp.getCode();
321: int skipArg = this Type == Type.void_type ? 1 : 0;
322: int arg_count = argTypes.length - skipArg;
323: boolean is_static = this Type == null || skipArg != 0;
324: int fix_arg_count = variable ? arg_count - (is_static ? 1 : 2)
325: : args.length;
326: Declaration argDecl = source == null ? null : source
327: .firstDecl();
328:
329: Scope scope;
330: if (parameterCopies == null)
331: scope = null;
332: else
333: scope = code.pushScope();
334:
335: for (int i = 0;; ++i) {
336: if (variable && i == fix_arg_count) {
337: arg_type = argTypes[arg_count - 1 + skipArg];
338: /*
339: if (arg_type == Compilation.scmListType)
340: {
341: gnu.kawa.functions.MakeList.compile(args, i, comp);
342: break;
343: }
344: */
345: code.emitPushInt(args.length - fix_arg_count);
346: arg_type = ((ArrayType) arg_type).getComponentType();
347: code.emitNewArray(arg_type);
348: }
349: if (i >= args.length)
350: break;
351: if (i >= fix_arg_count) {
352: code.emitDup(1); // dup array.
353: code.emitPushInt(i - fix_arg_count);
354: } else
355: arg_type = argDecl != null && (is_static || i > 0) ? argDecl
356: .getType()
357: : is_static ? argTypes[i + skipArg]
358: : i == 0 ? this Type : argTypes[i - 1];
359: Target target = StackTarget.getInstance(arg_type);
360: args[i].compileNotePosition(comp, target);
361: if (parameterCopies != null && parameterCopies[i] != null) {
362: Variable value = scope.addVariable(code, target
363: .getType(), argDecl != null ? argDecl.getName()
364: : "l_" + i);
365: parameterCopies[i].push(value);
366: code.emitDup();
367: code.emitStore(value);
368: }
369: if (i >= fix_arg_count)
370: code.emitArrayStore(arg_type);
371: if (argDecl != null && (is_static || i > 0))
372: argDecl = argDecl.nextDecl();
373: }
374:
375: // Restore the state of the captured parameters,
376: // so that a subsequent after a nested call use is possible.
377: if (parameterCopies != null)
378: for (int i = 0; i < arg_count; i++)
379: if (parameterCopies[i] != null)
380: parameterCopies[i].pop();
381:
382: if (scope != null)
383: code.popScope();
384: }
385:
386: public void compile(ApplyExp exp, Compilation comp, Target target) {
387: gnu.bytecode.CodeAttr code = comp.getCode();
388:
389: if (isConstructor()) {
390: ClassType type = method.getDeclaringClass();
391: code.emitNew(type);
392: code.emitDup(type);
393: }
394:
395: Expression[] args = exp.getArgs();
396: String arg_error = WrongArguments.checkArgCount(this ,
397: args.length);
398: if (arg_error != null)
399: comp.error('e', arg_error);
400:
401: try {
402: compile(
403: getStaticFlag() ? null : method.getDeclaringClass(),
404: args, comp, target);
405: } catch (VerificationError e) {
406: throw bossa.util.User.error(bossa.util.Location.make(exp),
407: e.getMessage());
408: }
409: }
410:
411: public void compile(Type this Type, Expression[] args,
412: Compilation comp, Target target) {
413: gnu.bytecode.CodeAttr code = comp.getCode();
414: compileArgs(args, this Type, argTypes, takesVarArgs(),
415: getName(), source, comp, parameterCopies);
416:
417: if (method == null)
418: code.emitPrimop(opcode(), args.length, retType);
419: else
420: code.emitInvokeMethod(method, opcode());
421: target.compileFromStack(comp, retType);
422: }
423:
424: public Type getParameterType(int index) {
425: if (!getStaticFlag()) {
426: if (index == 0)
427: return method.getDeclaringClass();
428: index--;
429: }
430: int lenTypes = argTypes.length;
431: if (index < lenTypes - 1)
432: return argTypes[index];
433: boolean varArgs = takesVarArgs();
434: if (index < lenTypes && !varArgs)
435: return argTypes[index];
436: // if (! varArgs) ERROR;
437: return ((ArrayType) argTypes[lenTypes - 1]).getComponentType();
438: }
439:
440: // This is null in JDK 1.1 and something else in JDK 1.2.
441: private static ClassLoader systemClassLoader = PrimProcedure.class
442: .getClassLoader();
443:
444: public static PrimProcedure getMethodFor(Procedure pproc,
445: Expression[] args) {
446: return getMethodFor(pproc, null, args, Interpreter
447: .getInterpreter());
448: }
449:
450: /** Search for a matching static method in a procedure's class.
451: * @return a PrimProcedure that is suitable, or null. */
452: public static PrimProcedure getMethodFor(Procedure pproc,
453: Declaration decl, Expression[] args, Interpreter interpreter) {
454: Class pclass = getProcedureClass(pproc);
455: if (pclass == null)
456: return null;
457: return getMethodFor(pclass, pproc.getName(), decl, args,
458: interpreter);
459: }
460:
461: public static Class getProcedureClass(Object pproc) {
462: Class procClass;
463: if (pproc instanceof gnu.expr.ModuleMethod)
464: procClass = ((ModuleMethod) pproc).module.getClass();
465: else
466: procClass = pproc.getClass();
467: try {
468: if (procClass.getClassLoader() == systemClassLoader)
469: return procClass;
470: } catch (SecurityException ex) {
471: }
472: return null;
473: }
474:
475: /** Get PrimProcedure for matching method in given class. */
476: public static PrimProcedure getMethodFor(Class procClass,
477: String name, Declaration decl, Expression[] args,
478: Interpreter interpreter) {
479: try {
480: java.lang.reflect.Method[] meths = procClass
481: .getDeclaredMethods();
482: java.lang.reflect.Method best = null;
483: Class[] bestTypes = null;
484: if (name == null)
485: return null;
486: String mangledName = decl == null || decl.field == null ? Compilation
487: .mangleName(name)
488: : decl.field.getName();
489: String mangledNameV = mangledName + "$V";
490: for (int i = meths.length; --i >= 0;) {
491: java.lang.reflect.Method meth = meths[i];
492: int mods = meth.getModifiers();
493: if ((mods & (Access.STATIC | Access.PUBLIC)) != (Access.STATIC | Access.PUBLIC)) {
494: if (decl == null || decl.base == null)
495: continue;
496: }
497: String mname = meth.getName();
498: boolean variable;
499: if (mname.equals("apply") || mname.equals(mangledName))
500: variable = false;
501: else if (mname.equals("apply$V")
502: || mname.equals(mangledNameV))
503: variable = true;
504: else
505: continue;
506: Class[] ptypes = meth.getParameterTypes();
507: if (variable ? ptypes.length - 1 > args.length
508: : ptypes.length != args.length)
509: continue;
510: // In the future, we may try to find the "best" match.
511: if (best != null)
512: return null;
513: best = meth;
514: bestTypes = ptypes;
515: }
516: if (best != null) {
517: PrimProcedure prproc = new PrimProcedure(best,
518: procClass, bestTypes, interpreter);
519: prproc.setName(name);
520: return prproc;
521: }
522: } catch (SecurityException ex) {
523: }
524: return null;
525: }
526:
527: public String getName() {
528: String name = super .getName();
529: if (name != null)
530: return name;
531: name = getVerboseName();
532: setName(name);
533: return name;
534: }
535:
536: public String getVerboseName() {
537: StringBuffer buf = new StringBuffer(100);
538: if (method == null) {
539: buf.append("<op ");
540: buf.append(op_code);
541: buf.append('>');
542: } else {
543: buf.append(method.getDeclaringClass().getName());
544: buf.append('.');
545: buf.append(method.getName());
546: }
547: buf.append('(');
548: for (int i = 0; i < argTypes.length; i++) {
549: if (i > 0)
550: buf.append(',');
551: buf.append(argTypes[i].getName());
552: }
553: buf.append(')');
554: return buf.toString();
555: }
556:
557: public String toString() {
558: StringBuffer buf = new StringBuffer(100);
559: buf.append(retType.getName());
560: buf.append(' ');
561: buf.append(getVerboseName());
562: return buf.toString();
563: }
564:
565: public void print(java.io.PrintWriter ps) {
566: ps.print("#<primitive procedure ");
567: ps.print(toString());
568: ps.print('>');
569: }
570: }
|