0001: /*****************************************************************************
0002: * *
0003: * This file is part of the BeanShell Java Scripting distribution. *
0004: * Documentation and updates may be found at http://www.beanshell.org/ *
0005: * *
0006: * Sun Public License Notice: *
0007: * *
0008: * The contents of this file are subject to the Sun Public License Version *
0009: * 1.0 (the "License"); you may not use this file except in compliance with *
0010: * the License. A copy of the License is available at http://www.sun.com *
0011: * *
0012: * The Original Code is BeanShell. The Initial Developer of the Original *
0013: * Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
0014: * (C) 2000. All Rights Reserved. *
0015: * *
0016: * GNU Public License Notice: *
0017: * *
0018: * Alternatively, the contents of this file may be used under the terms of *
0019: * the GNU Lesser General Public License (the "LGPL"), in which case the *
0020: * provisions of LGPL are applicable instead of those above. If you wish to *
0021: * allow use of your version of this file only under the terms of the LGPL *
0022: * and not to allow others to use your version of this file under the SPL, *
0023: * indicate your decision by deleting the provisions above and replace *
0024: * them with the notice and other provisions required by the LGPL. If you *
0025: * do not delete the provisions above, a recipient may use your version of *
0026: * this file under either the SPL or the LGPL. *
0027: * *
0028: * Patrick Niemeyer (pat@pat.net) *
0029: * Author of Learning Java, O'Reilly & Associates *
0030: * http://www.pat.net/~pat/ *
0031: * *
0032: *****************************************************************************/package org.gjt.sp.jedit.bsh;
0033:
0034: import org.gjt.sp.jedit.bsh.org.objectweb.asm.*;
0035: import org.gjt.sp.jedit.bsh.org.objectweb.asm.Type;
0036:
0037: import java.lang.reflect.*;
0038: import java.util.ArrayList;
0039: import java.util.List;
0040:
0041: /**
0042: ClassGeneratorUtil utilizes the ASM (www.objectweb.org) bytecode generator
0043: by Eric Bruneton in order to generate class "stubs" for BeanShell at
0044: runtime.
0045: <p>
0046:
0047: Stub classes contain all of the fields of a BeanShell scripted class
0048: as well as two "callback" references to BeanShell namespaces: one for
0049: static methods and one for instance methods. Methods of the class are
0050: delegators which invoke corresponding methods on either the static or
0051: instance bsh object and then unpack and return the results. The static
0052: namespace utilizes a static import to delegate variable access to the
0053: class' static fields. The instance namespace utilizes a dynamic import
0054: (i.e. mixin) to delegate variable access to the class' instance variables.
0055: <p>
0056:
0057: Constructors for the class delegate to the static initInstance() method of
0058: ClassGeneratorUtil to initialize new instances of the object. initInstance()
0059: invokes the instance intializer code (init vars and instance blocks) and
0060: then delegates to the corresponding scripted constructor method in the
0061: instance namespace. Constructors contain special switch logic which allows
0062: the BeanShell to control the calling of alternate constructors (this() or
0063: super() references) at runtime.
0064: <p>
0065:
0066: Specially named superclass delegator methods are also generated in order to
0067: allow BeanShell to access overridden methods of the superclass (which
0068: reflection does not normally allow).
0069: <p>
0070:
0071: @author Pat Niemeyer
0072: */
0073: /*
0074: Notes:
0075: It would not be hard to eliminate the use of org.objectweb.asm.Type from
0076: this class, making the distribution a tiny bit smaller.
0077: */
0078: public class ClassGeneratorUtil implements Constants {
0079: /** The name of the static field holding the reference to the bsh
0080: static This (the callback namespace for static methods) */
0081: static final String BSHSTATIC = "_bshStatic";
0082:
0083: /** The name of the instance field holding the reference to the bsh
0084: instance This (the callback namespace for instance methods) */
0085: static final String BSHTHIS = "_bshThis";
0086:
0087: /** The prefix for the name of the super delegate methods. e.g.
0088: _bshSuperfoo() is equivalent to super.foo() */
0089: static final String BSHSUPER = "_bshSuper";
0090:
0091: /** The bsh static namespace variable name of the instance initializer */
0092: static final String BSHINIT = "_bshInstanceInitializer";
0093:
0094: /** The bsh static namespace variable that holds the constructor methods */
0095: static final String BSHCONSTRUCTORS = "_bshConstructors";
0096:
0097: /** The switch branch number for the default constructor.
0098: The value -1 will cause the default branch to be taken. */
0099: static final int DEFAULTCONSTRUCTOR = -1;
0100:
0101: static final String OBJECT = "Ljava/lang/Object;";
0102:
0103: String className;
0104: /** fully qualified class name (with package) e.g. foo/bar/Blah */
0105: String fqClassName;
0106: Class super Class;
0107: String super ClassName;
0108: Class[] interfaces;
0109: Variable[] vars;
0110: Constructor[] super Constructors;
0111: DelayedEvalBshMethod[] constructors;
0112: DelayedEvalBshMethod[] methods;
0113: NameSpace classStaticNameSpace;
0114: Modifiers classModifiers;
0115: boolean isInterface;
0116:
0117: /**
0118: @param packageName e.g. "com.foo.bar"
0119: */
0120: public ClassGeneratorUtil(Modifiers classModifiers,
0121: String className, String packageName, Class super Class,
0122: Class[] interfaces, Variable[] vars,
0123: DelayedEvalBshMethod[] bshmethods,
0124: NameSpace classStaticNameSpace, boolean isInterface) {
0125: this .classModifiers = classModifiers;
0126: this .className = className;
0127: if (packageName != null)
0128: this .fqClassName = packageName.replace('.', '/') + "/"
0129: + className;
0130: else
0131: this .fqClassName = className;
0132: if (super Class == null)
0133: super Class = Object.class;
0134: this .super Class = super Class;
0135: this .super ClassName = Type.getInternalName(super Class);
0136: if (interfaces == null)
0137: interfaces = new Class[0];
0138: this .interfaces = interfaces;
0139: this .vars = vars;
0140: this .classStaticNameSpace = classStaticNameSpace;
0141: this .super Constructors = super Class.getDeclaredConstructors();
0142:
0143: // Split the methods into constructors and regular method lists
0144: List consl = new ArrayList();
0145: List methodsl = new ArrayList();
0146: String classBaseName = getBaseName(className); // for inner classes
0147: for (int i = 0; i < bshmethods.length; i++)
0148: if (bshmethods[i].getName().equals(classBaseName))
0149: consl.add(bshmethods[i]);
0150: else
0151: methodsl.add(bshmethods[i]);
0152:
0153: this .constructors = (DelayedEvalBshMethod[]) consl
0154: .toArray(new DelayedEvalBshMethod[0]);
0155: this .methods = (DelayedEvalBshMethod[]) methodsl
0156: .toArray(new DelayedEvalBshMethod[0]);
0157:
0158: try {
0159: classStaticNameSpace.setLocalVariable(BSHCONSTRUCTORS,
0160: constructors, false/*strict*/);
0161: } catch (UtilEvalError e) {
0162: throw new InterpreterError("can't set cons var");
0163: }
0164:
0165: this .isInterface = isInterface;
0166: }
0167:
0168: /**
0169: Generate the class bytecode for this class.
0170: */
0171: public byte[] generateClass() {
0172: // Force the class public for now...
0173: int classMods = getASMModifiers(classModifiers) | ACC_PUBLIC;
0174: if (isInterface)
0175: classMods |= ACC_INTERFACE;
0176:
0177: String[] interfaceNames = new String[interfaces.length];
0178: for (int i = 0; i < interfaces.length; i++)
0179: interfaceNames[i] = Type.getInternalName(interfaces[i]);
0180:
0181: String sourceFile = "BeanShell Generated via ASM (www.objectweb.org)";
0182: ClassWriter cw = new ClassWriter(false);
0183: cw.visit(classMods, fqClassName, super ClassName,
0184: interfaceNames, sourceFile);
0185:
0186: if (!isInterface) {
0187: // Generate the bsh instance 'This' reference holder field
0188: generateField(BSHTHIS + className,
0189: "Lorg/gjt/sp/jedit/bsh/This;", ACC_PUBLIC, cw);
0190:
0191: // Generate the static bsh static reference holder field
0192: generateField(BSHSTATIC + className,
0193: "Lorg/gjt/sp/jedit/bsh/This;", ACC_PUBLIC
0194: + ACC_STATIC, cw);
0195: }
0196:
0197: // Generate the fields
0198: for (int i = 0; i < vars.length; i++) {
0199: String type = vars[i].getTypeDescriptor();
0200:
0201: // Don't generate private or loosely typed fields
0202: // Note: loose types aren't currently parsed anyway...
0203: if (vars[i].hasModifier("private") || type == null)
0204: continue;
0205:
0206: int modifiers;
0207: if (isInterface)
0208: modifiers = ACC_PUBLIC | ACC_STATIC | ACC_FINAL;
0209: else
0210: modifiers = getASMModifiers(vars[i].getModifiers());
0211:
0212: generateField(vars[i].getName(), type, modifiers, cw);
0213: }
0214:
0215: // Generate the constructors
0216: boolean hasConstructor = false;
0217: for (int i = 0; i < constructors.length; i++) {
0218: // Don't generate private constructors
0219: if (constructors[i].hasModifier("private"))
0220: continue;
0221:
0222: int modifiers = getASMModifiers(constructors[i]
0223: .getModifiers());
0224: generateConstructor(i, constructors[i]
0225: .getParamTypeDescriptors(), modifiers, cw);
0226: hasConstructor = true;
0227: }
0228:
0229: // If no other constructors, generate a default constructor
0230: if (!isInterface && !hasConstructor)
0231: generateConstructor(DEFAULTCONSTRUCTOR/*index*/,
0232: new String[0], ACC_PUBLIC, cw);
0233:
0234: // Generate the delegate methods
0235: for (int i = 0; i < methods.length; i++) {
0236: String returnType = methods[i].getReturnTypeDescriptor();
0237:
0238: // Don't generate private /*or loosely return typed */ methods
0239: if (methods[i].hasModifier("private") /*|| returnType == null*/)
0240: continue;
0241:
0242: int modifiers = getASMModifiers(methods[i].getModifiers());
0243: if (isInterface)
0244: modifiers |= (ACC_PUBLIC | ACC_ABSTRACT);
0245:
0246: generateMethod(className, fqClassName,
0247: methods[i].getName(), returnType, methods[i]
0248: .getParamTypeDescriptors(), modifiers, cw);
0249:
0250: boolean isStatic = (modifiers & ACC_STATIC) > 0;
0251: boolean overridden = classContainsMethod(super Class,
0252: methods[i].getName(), methods[i]
0253: .getParamTypeDescriptors());
0254: if (!isStatic && overridden)
0255: generateSuperDelegateMethod(super ClassName, methods[i]
0256: .getName(), returnType, methods[i]
0257: .getParamTypeDescriptors(), modifiers, cw);
0258: }
0259:
0260: return cw.toByteArray();
0261: }
0262:
0263: /**
0264: Translate bsh.Modifiers into ASM modifier bitflags.
0265: */
0266: static int getASMModifiers(Modifiers modifiers) {
0267: int mods = 0;
0268: if (modifiers == null)
0269: return mods;
0270:
0271: if (modifiers.hasModifier("public"))
0272: mods += ACC_PUBLIC;
0273: if (modifiers.hasModifier("protected"))
0274: mods += ACC_PROTECTED;
0275: if (modifiers.hasModifier("static"))
0276: mods += ACC_STATIC;
0277: if (modifiers.hasModifier("synchronized"))
0278: mods += ACC_SYNCHRONIZED;
0279: if (modifiers.hasModifier("abstract"))
0280: mods += ACC_ABSTRACT;
0281:
0282: return mods;
0283: }
0284:
0285: /**
0286: Generate a field - static or instance.
0287: */
0288: static void generateField(String fieldName, String type,
0289: int modifiers, ClassWriter cw) {
0290: cw.visitField(modifiers, fieldName, type, null/*value*/);
0291: }
0292:
0293: /**
0294: Generate a delegate method - static or instance.
0295: The generated code packs the method arguments into an object array
0296: (wrapping primitive types in bsh.Primitive), invokes the static or
0297: instance namespace invokeMethod() method, and then unwraps / returns
0298: the result.
0299: */
0300: static void generateMethod(String className, String fqClassName,
0301: String methodName, String returnType, String[] paramTypes,
0302: int modifiers, ClassWriter cw) {
0303: String[] exceptions = null;
0304: boolean isStatic = (modifiers & ACC_STATIC) != 0;
0305:
0306: if (returnType == null) // map loose return type to Object
0307: returnType = OBJECT;
0308:
0309: String methodDescriptor = getMethodDescriptor(returnType,
0310: paramTypes);
0311:
0312: // Generate method body
0313: CodeVisitor cv = cw.visitMethod(modifiers, methodName,
0314: methodDescriptor, exceptions);
0315:
0316: if ((modifiers & ACC_ABSTRACT) != 0)
0317: return;
0318:
0319: // Generate code to push the BSHTHIS or BSHSTATIC field
0320: if (isStatic) {
0321: cv.visitFieldInsn(GETSTATIC, fqClassName, BSHSTATIC
0322: + className, "Lorg/gjt/sp/jedit/bsh/This;");
0323: } else {
0324: // Push 'this'
0325: cv.visitVarInsn(ALOAD, 0);
0326:
0327: // Get the instance field
0328: cv.visitFieldInsn(GETFIELD, fqClassName, BSHTHIS
0329: + className, "Lorg/gjt/sp/jedit/bsh/This;");
0330: }
0331:
0332: // Push the name of the method as a constant
0333: cv.visitLdcInsn(methodName);
0334:
0335: // Generate code to push arguments as an object array
0336: generateParameterReifierCode(paramTypes, isStatic, cv);
0337:
0338: // Push nulls for various args of invokeMethod
0339: cv.visitInsn(ACONST_NULL); // interpreter
0340: cv.visitInsn(ACONST_NULL); // callstack
0341: cv.visitInsn(ACONST_NULL); // callerinfo
0342:
0343: // Push the boolean constant 'true' (for declaredOnly)
0344: cv.visitInsn(ICONST_1);
0345:
0346: // Invoke the method This.invokeMethod( name, Class [] sig, boolean )
0347: cv.visitMethodInsn(INVOKEVIRTUAL, "org/gjt/sp/jedit/bsh/This",
0348: "invokeMethod", Type.getMethodDescriptor(Type
0349: .getType(Object.class), new Type[] {
0350: Type.getType(String.class),
0351: Type.getType(Object[].class),
0352: Type.getType(Interpreter.class),
0353: Type.getType(CallStack.class),
0354: Type.getType(SimpleNode.class),
0355: Type.getType(Boolean.TYPE) }));
0356:
0357: // Generate code to unwrap bsh Primitive types
0358: cv.visitMethodInsn(INVOKESTATIC,
0359: "org/gjt/sp/jedit/bsh/Primitive", "unwrap",
0360: "(Ljava/lang/Object;)Ljava/lang/Object;");
0361:
0362: // Generate code to return the value
0363: generateReturnCode(returnType, cv);
0364:
0365: // Need to calculate this... just fudging here for now.
0366: cv.visitMaxs(20, 20);
0367: }
0368:
0369: /**
0370: Generate a constructor.
0371: */
0372: void generateConstructor(int index, String[] paramTypes,
0373: int modifiers, ClassWriter cw) {
0374: /** offset after params of the args object [] var */
0375: final int argsVar = paramTypes.length + 1;
0376: /** offset after params of the ConstructorArgs var */
0377: final int consArgsVar = paramTypes.length + 2;
0378:
0379: String[] exceptions = null;
0380: String methodDescriptor = getMethodDescriptor("V", paramTypes);
0381:
0382: // Create this constructor method
0383: CodeVisitor cv = cw.visitMethod(modifiers, "<init>",
0384: methodDescriptor, exceptions);
0385:
0386: // Generate code to push arguments as an object array
0387: generateParameterReifierCode(paramTypes, false/*isStatic*/, cv);
0388: cv.visitVarInsn(ASTORE, argsVar);
0389:
0390: // Generate the code implementing the alternate constructor switch
0391: generateConstructorSwitch(index, argsVar, consArgsVar, cv);
0392:
0393: // Generate code to invoke the ClassGeneratorUtil initInstance() method
0394:
0395: // push 'this'
0396: cv.visitVarInsn(ALOAD, 0);
0397:
0398: // Push the class/constructor name as a constant
0399: cv.visitLdcInsn(className);
0400:
0401: // Push arguments as an object array
0402: cv.visitVarInsn(ALOAD, argsVar);
0403:
0404: // invoke the initInstance() method
0405: cv
0406: .visitMethodInsn(INVOKESTATIC,
0407: "org/gjt/sp/jedit/bsh/ClassGeneratorUtil",
0408: "initInstance",
0409: "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V");
0410:
0411: cv.visitInsn(RETURN);
0412:
0413: // Need to calculate this... just fudging here for now.
0414: cv.visitMaxs(20, 20);
0415: }
0416:
0417: /**
0418: Generate a switch with a branch for each possible alternate
0419: constructor. This includes all superclass constructors and all
0420: constructors of this class. The default branch of this switch is the
0421: default superclass constructor.
0422: <p>
0423: This method also generates the code to call the static
0424: ClassGeneratorUtil
0425: getConstructorArgs() method which inspects the scripted constructor to
0426: find the alternate constructor signature (if any) and evalute the
0427: arguments at runtime. The getConstructorArgs() method returns the
0428: actual arguments as well as the index of the constructor to call.
0429: */
0430: void generateConstructorSwitch(int consIndex, int argsVar,
0431: int consArgsVar, CodeVisitor cv) {
0432: Label defaultLabel = new Label();
0433: Label endLabel = new Label();
0434: int cases = super Constructors.length + constructors.length;
0435:
0436: Label[] labels = new Label[cases];
0437: for (int i = 0; i < cases; i++)
0438: labels[i] = new Label();
0439:
0440: // Generate code to call ClassGeneratorUtil to get our switch index
0441: // and give us args...
0442:
0443: // push super class name
0444: cv.visitLdcInsn(super Class.getName()); // use superClassName var?
0445:
0446: // push class static This object
0447: cv.visitFieldInsn(GETSTATIC, fqClassName,
0448: BSHSTATIC + className, "Lorg/gjt/sp/jedit/bsh/This;");
0449:
0450: // push args
0451: cv.visitVarInsn(ALOAD, argsVar);
0452:
0453: // push this constructor index number onto stack
0454: cv.visitIntInsn(BIPUSH, consIndex);
0455:
0456: // invoke the ClassGeneratorUtil getConstructorsArgs() method
0457: cv
0458: .visitMethodInsn(
0459: INVOKESTATIC,
0460: "org/gjt/sp/jedit/bsh/ClassGeneratorUtil",
0461: "getConstructorArgs",
0462: "(Ljava/lang/String;Lorg/gjt/sp/jedit/bsh/This;[Ljava/lang/Object;I)"
0463: + "Lorg/gjt/sp/jedit/bsh/ClassGeneratorUtil$ConstructorArgs;");
0464:
0465: // store ConstructorArgs in consArgsVar
0466: cv.visitVarInsn(ASTORE, consArgsVar);
0467:
0468: // Get the ConstructorArgs selector field from ConstructorArgs
0469:
0470: // push ConstructorArgs
0471: cv.visitVarInsn(ALOAD, consArgsVar);
0472: cv
0473: .visitFieldInsn(
0474: GETFIELD,
0475: "org/gjt/sp/jedit/bsh/ClassGeneratorUtil$ConstructorArgs",
0476: "selector", "I");
0477:
0478: // start switch
0479: cv.visitTableSwitchInsn(0/*min*/, cases - 1/*max*/,
0480: defaultLabel, labels);
0481:
0482: // generate switch body
0483: int index = 0;
0484: for (int i = 0; i < super Constructors.length; i++, index++)
0485: doSwitchBranch(index, super ClassName,
0486: getTypeDescriptors(super Constructors[i]
0487: .getParameterTypes()), endLabel, labels,
0488: consArgsVar, cv);
0489: for (int i = 0; i < constructors.length; i++, index++)
0490: doSwitchBranch(index, fqClassName, constructors[i]
0491: .getParamTypeDescriptors(), endLabel, labels,
0492: consArgsVar, cv);
0493:
0494: // generate the default branch of switch
0495: cv.visitLabel(defaultLabel);
0496: // default branch always invokes no args super
0497: cv.visitVarInsn(ALOAD, 0); // push 'this'
0498: cv.visitMethodInsn(INVOKESPECIAL, super ClassName, "<init>",
0499: "()V");
0500:
0501: // done with switch
0502: cv.visitLabel(endLabel);
0503: }
0504:
0505: /*
0506: Generate a branch of the constructor switch. This method is called by
0507: generateConstructorSwitch.
0508: The code generated by this method assumes that the argument array is
0509: on the stack.
0510: */
0511: static void doSwitchBranch(int index, String targetClassName,
0512: String[] paramTypes, Label endLabel, Label[] labels,
0513: int consArgsVar, CodeVisitor cv) {
0514: cv.visitLabel(labels[index]);
0515: //cv.visitLineNumber( index, labels[index] );
0516: cv.visitVarInsn(ALOAD, 0); // push this before args
0517:
0518: // Unload the arguments from the ConstructorArgs object
0519: for (int i = 0; i < paramTypes.length; i++) {
0520: String type = paramTypes[i];
0521: String method = null;
0522: if (type.equals("Z"))
0523: method = "getBoolean";
0524: else if (type.equals("B"))
0525: method = "getByte";
0526: else if (type.equals("C"))
0527: method = "getChar";
0528: else if (type.equals("S"))
0529: method = "getShort";
0530: else if (type.equals("I"))
0531: method = "getInt";
0532: else if (type.equals("J"))
0533: method = "getLong";
0534: else if (type.equals("D"))
0535: method = "getDouble";
0536: else if (type.equals("F"))
0537: method = "getFloat";
0538: else
0539: method = "getObject";
0540:
0541: // invoke the iterator method on the ConstructorArgs
0542: cv.visitVarInsn(ALOAD, consArgsVar); // push the ConstructorArgs
0543: String className = "org/gjt/sp/jedit/bsh/ClassGeneratorUtil$ConstructorArgs";
0544: String retType;
0545: if (method.equals("getObject"))
0546: retType = OBJECT;
0547: else
0548: retType = type;
0549: cv.visitMethodInsn(INVOKEVIRTUAL, className, method, "()"
0550: + retType);
0551: // if it's an object type we must do a check cast
0552: if (method.equals("getObject"))
0553: cv
0554: .visitTypeInsn(CHECKCAST,
0555: descriptorToClassName(type));
0556: }
0557:
0558: // invoke the constructor for this branch
0559: String descriptor = getMethodDescriptor("V", paramTypes);
0560: cv.visitMethodInsn(INVOKESPECIAL, targetClassName, "<init>",
0561: descriptor);
0562: cv.visitJumpInsn(GOTO, endLabel);
0563: }
0564:
0565: static String getMethodDescriptor(String returnType,
0566: String[] paramTypes) {
0567: StringBuffer sb = new StringBuffer("(");
0568: for (int i = 0; i < paramTypes.length; i++)
0569: sb.append(paramTypes[i]);
0570: sb.append(")" + returnType);
0571: return sb.toString();
0572: }
0573:
0574: /**
0575: Generate a superclass method delegate accessor method.
0576: These methods are specially named methods which allow access to
0577: overridden methods of the superclass (which the Java reflection API
0578: normally does not allow).
0579: */
0580: // Maybe combine this with generateMethod()
0581: static void generateSuperDelegateMethod(String super ClassName,
0582: String methodName, String returnType, String[] paramTypes,
0583: int modifiers, ClassWriter cw) {
0584: String[] exceptions = null;
0585:
0586: if (returnType == null) // map loose return to Object
0587: returnType = OBJECT;
0588:
0589: String methodDescriptor = getMethodDescriptor(returnType,
0590: paramTypes);
0591:
0592: // Add method body
0593: CodeVisitor cv = cw.visitMethod(modifiers, "_bshSuper"
0594: + methodName, methodDescriptor, exceptions);
0595:
0596: cv.visitVarInsn(ALOAD, 0);
0597: // Push vars
0598: int localVarIndex = 1;
0599: for (int i = 0; i < paramTypes.length; ++i) {
0600: if (isPrimitive(paramTypes[i]))
0601: cv.visitVarInsn(ILOAD, localVarIndex);
0602: else
0603: cv.visitVarInsn(ALOAD, localVarIndex);
0604: localVarIndex += ((paramTypes[i].equals("D") || paramTypes[i]
0605: .equals("J")) ? 2 : 1);
0606: }
0607:
0608: cv.visitMethodInsn(INVOKESPECIAL, super ClassName, methodName,
0609: methodDescriptor);
0610:
0611: generatePlainReturnCode(returnType, cv);
0612:
0613: // Need to calculate this... just fudging here for now.
0614: cv.visitMaxs(20, 20);
0615: }
0616:
0617: boolean classContainsMethod(Class clas, String methodName,
0618: String[] paramTypes) {
0619: while (clas != null) {
0620: Method[] methods = clas.getDeclaredMethods();
0621: for (int i = 0; i < methods.length; i++) {
0622: if (methods[i].getName().equals(methodName)) {
0623: String[] methodParamTypes = getTypeDescriptors(methods[i]
0624: .getParameterTypes());
0625: boolean found = true;
0626: for (int j = 0; j < methodParamTypes.length; j++) {
0627: if (!paramTypes[j].equals(methodParamTypes[j])) {
0628: found = false;
0629: break;
0630: }
0631: }
0632: if (found)
0633: return true;
0634: }
0635: }
0636:
0637: clas = clas.getSuperclass();
0638: }
0639:
0640: return false;
0641: }
0642:
0643: /**
0644: Generate return code for a normal bytecode
0645: */
0646: static void generatePlainReturnCode(String returnType,
0647: CodeVisitor cv) {
0648: if (returnType.equals("V"))
0649: cv.visitInsn(RETURN);
0650: else if (isPrimitive(returnType)) {
0651: int opcode = IRETURN;
0652: if (returnType.equals("D"))
0653: opcode = DRETURN;
0654: else if (returnType.equals("F"))
0655: opcode = FRETURN;
0656: else if (returnType.equals("J")) //long
0657: opcode = LRETURN;
0658:
0659: cv.visitInsn(opcode);
0660: } else {
0661: cv.visitTypeInsn(CHECKCAST,
0662: descriptorToClassName(returnType));
0663: cv.visitInsn(ARETURN);
0664: }
0665: }
0666:
0667: /**
0668: Generates the code to reify the arguments of the given method.
0669: For a method "int m (int i, String s)", this code is the bytecode
0670: corresponding to the "new Object[] { new bsh.Primitive(i), s }"
0671: expression.
0672:
0673: @author Eric Bruneton
0674: @author Pat Niemeyer
0675: @param cv the code visitor to be used to generate the bytecode.
0676: @param isStatic the enclosing methods is static
0677: */
0678: public static void generateParameterReifierCode(
0679: String[] paramTypes, boolean isStatic, final CodeVisitor cv) {
0680: cv.visitIntInsn(SIPUSH, paramTypes.length);
0681: cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
0682: int localVarIndex = isStatic ? 0 : 1;
0683: for (int i = 0; i < paramTypes.length; ++i) {
0684: String param = paramTypes[i];
0685: cv.visitInsn(DUP);
0686: cv.visitIntInsn(SIPUSH, i);
0687: if (isPrimitive(param)) {
0688: int opcode;
0689: if (param.equals("F")) {
0690: opcode = FLOAD;
0691: } else if (param.equals("D")) {
0692: opcode = DLOAD;
0693: } else if (param.equals("J")) {
0694: opcode = LLOAD;
0695: } else {
0696: opcode = ILOAD;
0697: }
0698:
0699: String type = "org/gjt/sp/jedit/bsh/Primitive";
0700: cv.visitTypeInsn(NEW, type);
0701: cv.visitInsn(DUP);
0702: cv.visitVarInsn(opcode, localVarIndex);
0703: String desc = param; // ok?
0704: cv.visitMethodInsn(INVOKESPECIAL, type, "<init>", "("
0705: + desc + ")V");
0706: } else {
0707: // Technically incorrect here - we need to wrap null values
0708: // as bsh.Primitive.NULL. However the This.invokeMethod()
0709: // will do that much for us.
0710: // We need to generate a conditional here to test for null
0711: // and return Primitive.NULL
0712: cv.visitVarInsn(ALOAD, localVarIndex);
0713: }
0714: cv.visitInsn(AASTORE);
0715: localVarIndex += ((param.equals("D") || param.equals("J")) ? 2
0716: : 1);
0717: }
0718: }
0719:
0720: /**
0721: Generates the code to unreify the result of the given method. For a
0722: method "int m (int i, String s)", this code is the bytecode
0723: corresponding to the "((Integer)...).intValue()" expression.
0724:
0725: @param cv the code visitor to be used to generate the bytecode.
0726: @author Eric Bruneton
0727: @author Pat Niemeyer
0728: */
0729: public static void generateReturnCode(String returnType,
0730: CodeVisitor cv) {
0731: if (returnType.equals("V")) {
0732: cv.visitInsn(POP);
0733: cv.visitInsn(RETURN);
0734: } else if (isPrimitive(returnType)) {
0735: int opcode = IRETURN;
0736: String type;
0737: String meth;
0738: if (returnType.equals("B")) {
0739: type = "java/lang/Byte";
0740: meth = "byteValue";
0741: } else if (returnType.equals("I")) {
0742: type = "java/lang/Integer";
0743: meth = "intValue";
0744: } else if (returnType.equals("Z")) {
0745: type = "java/lang/Boolean";
0746: meth = "booleanValue";
0747: } else if (returnType.equals("D")) {
0748: opcode = DRETURN;
0749: type = "java/lang/Double";
0750: meth = "doubleValue";
0751: } else if (returnType.equals("F")) {
0752: opcode = FRETURN;
0753: type = "java/lang/Float";
0754: meth = "floatValue";
0755: } else if (returnType.equals("J")) {
0756: opcode = LRETURN;
0757: type = "java/lang/Long";
0758: meth = "longValue";
0759: } else if (returnType.equals("C")) {
0760: type = "java/lang/Character";
0761: meth = "charValue";
0762: } else /*if (returnType.equals("S") )*/{
0763: type = "java/lang/Short";
0764: meth = "shortValue";
0765: }
0766:
0767: String desc = returnType;
0768: cv.visitTypeInsn(CHECKCAST, type); // type is correct here
0769: cv.visitMethodInsn(INVOKEVIRTUAL, type, meth, "()" + desc);
0770: cv.visitInsn(opcode);
0771: } else {
0772: cv.visitTypeInsn(CHECKCAST,
0773: descriptorToClassName(returnType));
0774: cv.visitInsn(ARETURN);
0775: }
0776: }
0777:
0778: /**
0779: Evaluate the arguments (if any) for the constructor specified by
0780: the constructor index. Return the ConstructorArgs object which
0781: contains the actual arguments to the alternate constructor and also the
0782: index of that constructor for the constructor switch.
0783:
0784: @param consArgs the arguments to the constructor. These are necessary in
0785: the evaluation of the alt constructor args. e.g. Foo(a) { super(a); }
0786: @return the ConstructorArgs object containing a constructor selector
0787: and evaluated arguments for the alternate constructor
0788: */
0789: public static ConstructorArgs getConstructorArgs(
0790: String super ClassName, This classStaticThis,
0791: Object[] consArgs, int index) {
0792: DelayedEvalBshMethod[] constructors;
0793: try {
0794: constructors = (DelayedEvalBshMethod[]) classStaticThis
0795: .getNameSpace().getVariable(BSHCONSTRUCTORS);
0796: } catch (Exception e) {
0797: throw new InterpreterError(
0798: "unable to get instance initializer: " + e);
0799: }
0800:
0801: if (index == DEFAULTCONSTRUCTOR) // auto-gen default constructor
0802: return ConstructorArgs.DEFAULT; // use default super constructor
0803:
0804: DelayedEvalBshMethod constructor = constructors[index];
0805:
0806: if (constructor.methodBody.jjtGetNumChildren() == 0)
0807: return ConstructorArgs.DEFAULT; // use default super constructor
0808:
0809: // Determine if the constructor calls this() or super()
0810: String altConstructor = null;
0811: BSHArguments argsNode = null;
0812: SimpleNode firstStatement = (SimpleNode) constructor.methodBody
0813: .jjtGetChild(0);
0814: if (firstStatement instanceof BSHPrimaryExpression)
0815: firstStatement = (SimpleNode) firstStatement.jjtGetChild(0);
0816: if (firstStatement instanceof BSHMethodInvocation) {
0817: BSHMethodInvocation methodNode = (BSHMethodInvocation) firstStatement;
0818: BSHAmbiguousName methodName = methodNode.getNameNode();
0819: if (methodName.text.equals("super")
0820: || methodName.text.equals("this")) {
0821: altConstructor = methodName.text;
0822: argsNode = methodNode.getArgsNode();
0823: }
0824: }
0825:
0826: if (altConstructor == null)
0827: return ConstructorArgs.DEFAULT; // use default super constructor
0828:
0829: // Make a tmp namespace to hold the original constructor args for
0830: // use in eval of the parameters node
0831: NameSpace consArgsNameSpace = new NameSpace(classStaticThis
0832: .getNameSpace(), "consArgs");
0833: String[] consArgNames = constructor.getParameterNames();
0834: Class[] consArgTypes = constructor.getParameterTypes();
0835: for (int i = 0; i < consArgs.length; i++) {
0836: try {
0837: consArgsNameSpace
0838: .setTypedVariable(consArgNames[i],
0839: consArgTypes[i], consArgs[i], null/*modifiers*/);
0840: } catch (UtilEvalError e) {
0841: throw new InterpreterError(
0842: "err setting local cons arg:" + e);
0843: }
0844: }
0845:
0846: // evaluate the args
0847:
0848: CallStack callstack = new CallStack();
0849: callstack.push(consArgsNameSpace);
0850: Object[] args = null;
0851: Interpreter interpreter = classStaticThis.declaringInterpreter;
0852:
0853: try {
0854: args = argsNode.getArguments(callstack, interpreter);
0855: } catch (EvalError e) {
0856: throw new InterpreterError(
0857: "Error evaluating constructor args: " + e);
0858: }
0859:
0860: Class[] argTypes = Types.getTypes(args);
0861: args = Primitive.unwrap(args);
0862: Class super Class = interpreter.getClassManager().classForName(
0863: super ClassName);
0864: if (super Class == null)
0865: throw new InterpreterError("can't find superclass: "
0866: + super ClassName);
0867: Constructor[] super Cons = super Class.getDeclaredConstructors();
0868:
0869: // find the matching super() constructor for the args
0870: if (altConstructor.equals("super")) {
0871: int i = Reflect.findMostSpecificConstructorIndex(argTypes,
0872: super Cons);
0873: if (i == -1)
0874: throw new InterpreterError(
0875: "can't find constructor for args!");
0876: return new ConstructorArgs(i, args);
0877: }
0878:
0879: // find the matching this() constructor for the args
0880: Class[][] candidates = new Class[constructors.length][];
0881: for (int i = 0; i < candidates.length; i++)
0882: candidates[i] = constructors[i].getParameterTypes();
0883: int i = Reflect.findMostSpecificSignature(argTypes, candidates);
0884: if (i == -1)
0885: throw new InterpreterError(
0886: "can't find constructor for args 2!");
0887: // this() constructors come after super constructors in the table
0888:
0889: int selector = i + super Cons.length;
0890: int ourSelector = index + super Cons.length;
0891:
0892: // Are we choosing ourselves recursively through a this() reference?
0893: if (selector == ourSelector)
0894: throw new InterpreterError("Recusive constructor call.");
0895:
0896: return new ConstructorArgs(selector, args);
0897: }
0898:
0899: /**
0900: Initialize an instance of the class.
0901: This method is called from the generated class constructor to evaluate
0902: the instance initializer and scripted constructor in the instance
0903: namespace.
0904: */
0905: public static void initInstance(Object instance, String className,
0906: Object[] args) {
0907: Class[] sig = Types.getTypes(args);
0908: CallStack callstack = new CallStack();
0909: Interpreter interpreter;
0910: NameSpace instanceNameSpace;
0911:
0912: // check to see if the instance has already been initialized
0913: // (the case if using a this() alternate constuctor)
0914: This instanceThis = getClassInstanceThis(instance, className);
0915:
0916: // XXX clean up this conditional
0917: if (instanceThis == null) {
0918: // Create the instance 'This' namespace, set it on the object
0919: // instance and invoke the instance initializer
0920:
0921: // Get the static This reference from the proto-instance
0922: This classStaticThis = getClassStaticThis(instance
0923: .getClass(), className);
0924: interpreter = classStaticThis.declaringInterpreter;
0925:
0926: // Get the instance initializer block from the static This
0927: BSHBlock instanceInitBlock;
0928: try {
0929: instanceInitBlock = (BSHBlock) classStaticThis
0930: .getNameSpace().getVariable(BSHINIT);
0931: } catch (Exception e) {
0932: throw new InterpreterError(
0933: "unable to get instance initializer: " + e);
0934: }
0935:
0936: // Create the instance namespace
0937: instanceNameSpace = new NameSpace(classStaticThis
0938: .getNameSpace(), className);
0939: instanceNameSpace.isClass = true;
0940:
0941: // Set the instance This reference on the instance
0942: instanceThis = instanceNameSpace.getThis(interpreter);
0943: try {
0944: LHS lhs = Reflect.getLHSObjectField(instance, BSHTHIS
0945: + className);
0946: lhs.assign(instanceThis, false/*strict*/);
0947: } catch (Exception e) {
0948: throw new InterpreterError("Error in class gen setup: "
0949: + e);
0950: }
0951:
0952: // Give the instance space its object import
0953: instanceNameSpace.setClassInstance(instance);
0954:
0955: // should use try/finally here to pop ns
0956: callstack.push(instanceNameSpace);
0957:
0958: // evaluate the instance portion of the block in it
0959: try { // Evaluate the initializer block
0960: instanceInitBlock
0961: .evalBlock(
0962: callstack,
0963: interpreter,
0964: true/*override*/,
0965: ClassGeneratorImpl.ClassNodeFilter.CLASSINSTANCE);
0966: } catch (Exception e) {
0967: throw new InterpreterError(
0968: "Error in class initialization: " + e);
0969: }
0970:
0971: callstack.pop();
0972:
0973: } else {
0974: // The object instance has already been initialzed by another
0975: // constructor. Fall through to invoke the constructor body below.
0976: interpreter = instanceThis.declaringInterpreter;
0977: instanceNameSpace = instanceThis.getNameSpace();
0978: }
0979:
0980: // invoke the constructor method from the instanceThis
0981:
0982: String constructorName = getBaseName(className);
0983: try {
0984: // Find the constructor (now in the instance namespace)
0985: BshMethod constructor = instanceNameSpace.getMethod(
0986: constructorName, sig, true/*declaredOnly*/);
0987:
0988: // if args, we must have constructor
0989: if (args.length > 0 && constructor == null)
0990: throw new InterpreterError("Can't find constructor: "
0991: + className);
0992:
0993: // Evaluate the constructor
0994: if (constructor != null)
0995: constructor
0996: .invoke(args, interpreter, callstack,
0997: null/*callerInfo*/, false/*overrideNameSpace*/);
0998: } catch (Exception e) {
0999: if (e instanceof TargetError)
1000: e = (Exception) ((TargetError) e).getTarget();
1001: if (e instanceof InvocationTargetException)
1002: e = (Exception) ((InvocationTargetException) e)
1003: .getTargetException();
1004: e.printStackTrace(System.err);
1005: throw new InterpreterError(
1006: "Error in class initialization: " + e);
1007: }
1008: }
1009:
1010: /**
1011: Get the static bsh namespace field from the class.
1012: @param className may be the name of clas itself or a superclass of clas.
1013: */
1014: static This getClassStaticThis(Class clas, String className) {
1015: try {
1016: return (This) Reflect.getStaticFieldValue(clas, BSHSTATIC
1017: + className);
1018: } catch (Exception e) {
1019: throw new InterpreterError(
1020: "Unable to get class static space: " + e);
1021: }
1022: }
1023:
1024: /**
1025: Get the instance bsh namespace field from the object instance.
1026: @return the class instance This object or null if the object has not
1027: been initialized.
1028: */
1029: static This getClassInstanceThis(Object instance, String className) {
1030: try {
1031: Object o = Reflect.getObjectFieldValue(instance, BSHTHIS
1032: + className);
1033: return (This) Primitive.unwrap(o); // unwrap Primitive.Null to null
1034: } catch (Exception e) {
1035: throw new InterpreterError(
1036: "Generated class: Error getting This" + e);
1037: }
1038: }
1039:
1040: /**
1041: Does the type descriptor string describe a primitive type?
1042: */
1043: private static boolean isPrimitive(String typeDescriptor) {
1044: return typeDescriptor.length() == 1; // right?
1045: }
1046:
1047: static String[] getTypeDescriptors(Class[] cparams) {
1048: String[] sa = new String[cparams.length];
1049: for (int i = 0; i < sa.length; i++)
1050: sa[i] = BSHType.getTypeDescriptor(cparams[i]);
1051: return sa;
1052: }
1053:
1054: /**
1055: If a non-array object type, remove the prefix "L" and suffix ";".
1056: */
1057: // Can this be factored out...?
1058: // Should be be adding the L...; here instead?
1059: private static String descriptorToClassName(String s) {
1060: if (s.startsWith("[") || !s.startsWith("L"))
1061: return s;
1062: return s.substring(1, s.length() - 1);
1063: }
1064:
1065: private static String getBaseName(String className) {
1066: int i = className.indexOf("$");
1067: if (i == -1)
1068: return className;
1069:
1070: return className.substring(i + 1);
1071: }
1072:
1073: /**
1074: A ConstructorArgs object holds evaluated arguments for a constructor
1075: call as well as the index of a possible alternate selector to invoke.
1076: This object is used by the constructor switch.
1077: @see #generateConstructor( int , String [] , int , ClassWriter )
1078: */
1079: public static class ConstructorArgs {
1080: /** A ConstructorArgs which calls the default constructor */
1081: public static ConstructorArgs DEFAULT = new ConstructorArgs();
1082:
1083: public int selector = DEFAULTCONSTRUCTOR;
1084: Object[] args;
1085: int arg = 0;
1086:
1087: /**
1088: The index of the constructor to call.
1089: */
1090:
1091: ConstructorArgs() {
1092: }
1093:
1094: ConstructorArgs(int selector, Object[] args) {
1095: this .selector = selector;
1096: this .args = args;
1097: }
1098:
1099: Object next() {
1100: return args[arg++];
1101: }
1102:
1103: public boolean getBoolean() {
1104: return ((Boolean) next()).booleanValue();
1105: }
1106:
1107: public byte getByte() {
1108: return ((Byte) next()).byteValue();
1109: }
1110:
1111: public char getChar() {
1112: return ((Character) next()).charValue();
1113: }
1114:
1115: public short getShort() {
1116: return ((Short) next()).shortValue();
1117: }
1118:
1119: public int getInt() {
1120: return ((Integer) next()).intValue();
1121: }
1122:
1123: public long getLong() {
1124: return ((Long) next()).longValue();
1125: }
1126:
1127: public double getDouble() {
1128: return ((Double) next()).doubleValue();
1129: }
1130:
1131: public float getFloat() {
1132: return ((Float) next()).floatValue();
1133: }
1134:
1135: public Object getObject() {
1136: return next();
1137: }
1138: }
1139: }
|