KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > bsh > ClassGeneratorUtil


1 /*****************************************************************************
2  * *
3  * This file is part of the BeanShell Java Scripting distribution. *
4  * Documentation and updates may be found at http://www.beanshell.org/ *
5  * *
6  * Sun Public License Notice: *
7  * *
8  * The contents of this file are subject to the Sun Public License Version *
9  * 1.0 (the "License"); you may not use this file except in compliance with *
10  * the License. A copy of the License is available at http://www.sun.com *
11  * *
12  * The Original Code is BeanShell. The Initial Developer of the Original *
13  * Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
14  * (C) 2000. All Rights Reserved. *
15  * *
16  * GNU Public License Notice: *
17  * *
18  * Alternatively, the contents of this file may be used under the terms of *
19  * the GNU Lesser General Public License (the "LGPL"), in which case the *
20  * provisions of LGPL are applicable instead of those above. If you wish to *
21  * allow use of your version of this file only under the terms of the LGPL *
22  * and not to allow others to use your version of this file under the SPL, *
23  * indicate your decision by deleting the provisions above and replace *
24  * them with the notice and other provisions required by the LGPL. If you *
25  * do not delete the provisions above, a recipient may use your version of *
26  * this file under either the SPL or the LGPL. *
27  * *
28  * Patrick Niemeyer (pat@pat.net) *
29  * Author of Learning Java, O'Reilly & Associates *
30  * http://www.pat.net/~pat/ *
31  * *
32  *****************************************************************************/

33
34 package bsh;
35
36 import bsh.org.objectweb.asm.*;
37 import bsh.org.objectweb.asm.Type;
38
39 import java.lang.reflect.*;
40 import java.util.ArrayList JavaDoc;
41 import java.util.List JavaDoc;
42
43 /**
44     ClassGeneratorUtil utilizes the ASM (www.objectweb.org) bytecode generator
45     by Eric Bruneton in order to generate class "stubs" for BeanShell at
46     runtime.
47     <p>
48
49     Stub classes contain all of the fields of a BeanShell scripted class
50     as well as two "callback" references to BeanShell namespaces: one for
51     static methods and one for instance methods. Methods of the class are
52     delegators which invoke corresponding methods on either the static or
53     instance bsh object and then unpack and return the results. The static
54     namespace utilizes a static import to delegate variable access to the
55     class' static fields. The instance namespace utilizes a dynamic import
56     (i.e. mixin) to delegate variable access to the class' instance variables.
57     <p>
58
59     Constructors for the class delegate to the static initInstance() method of
60     ClassGeneratorUtil to initialize new instances of the object. initInstance()
61     invokes the instance intializer code (init vars and instance blocks) and
62     then delegates to the corresponding scripted constructor method in the
63     instance namespace. Constructors contain special switch logic which allows
64     the BeanShell to control the calling of alternate constructors (this() or
65     super() references) at runtime.
66     <p>
67
68     Specially named superclass delegator methods are also generated in order to
69     allow BeanShell to access overridden methods of the superclass (which
70     reflection does not normally allow).
71     <p>
72
73     @author Pat Niemeyer
74 */

75 /*
76     Notes:
77     It would not be hard to eliminate the use of org.objectweb.asm.Type from
78     this class, making the distribution a tiny bit smaller.
79 */

80 public class ClassGeneratorUtil implements Constants
81 {
82     /** The name of the static field holding the reference to the bsh
83          static This (the callback namespace for static methods) */

84     static final String JavaDoc BSHSTATIC="_bshStatic";
85
86     /** The name of the instance field holding the reference to the bsh
87          instance This (the callback namespace for instance methods) */

88     static final String JavaDoc BSHTHIS="_bshThis";
89
90     /** The prefix for the name of the super delegate methods. e.g.
91         _bshSuperfoo() is equivalent to super.foo() */

92     static final String JavaDoc BSHSUPER="_bshSuper";
93
94     /** The bsh static namespace variable name of the instance initializer */
95     static final String JavaDoc BSHINIT="_bshInstanceInitializer";
96
97     /** The bsh static namespace variable that holds the constructor methods */
98     static final String JavaDoc BSHCONSTRUCTORS="_bshConstructors";
99
100     /** The switch branch number for the default constructor.
101         The value -1 will cause the default branch to be taken. */

102     static final int DEFAULTCONSTRUCTOR = -1;
103
104     static final String JavaDoc OBJECT= "Ljava/lang/Object;";
105
106     String JavaDoc className;
107     /** fully qualified class name (with package) e.g. foo/bar/Blah */
108     String JavaDoc fqClassName;
109     Class JavaDoc superClass;
110     String JavaDoc superClassName;
111     Class JavaDoc [] interfaces;
112     Variable [] vars;
113     Constructor [] superConstructors;
114     DelayedEvalBshMethod [] constructors;
115     DelayedEvalBshMethod [] methods;
116     NameSpace classStaticNameSpace;
117     Modifiers classModifiers;
118     boolean isInterface;
119
120     /**
121         @param packageName e.g. "com.foo.bar"
122     */

123     public ClassGeneratorUtil(
124         Modifiers classModifiers, String JavaDoc className, String JavaDoc packageName,
125         Class JavaDoc superClass, Class JavaDoc [] interfaces, Variable [] vars,
126         DelayedEvalBshMethod [] bshmethods, NameSpace classStaticNameSpace,
127         boolean isInterface
128     )
129     {
130         this.classModifiers = classModifiers;
131         this.className = className;
132         if ( packageName != null )
133             this.fqClassName = packageName.replace('.','/') + "/" + className;
134         else
135             this.fqClassName = className;
136         if ( superClass == null )
137             superClass = Object JavaDoc.class;
138         this.superClass = superClass;
139         this.superClassName = Type.getInternalName( superClass );
140         if ( interfaces == null )
141             interfaces = new Class JavaDoc[0];
142         this.interfaces = interfaces;
143         this.vars = vars;
144         this.classStaticNameSpace = classStaticNameSpace;
145         this.superConstructors = superClass.getDeclaredConstructors();
146
147         // Split the methods into constructors and regular method lists
148
List JavaDoc consl = new ArrayList JavaDoc();
149         List JavaDoc methodsl = new ArrayList JavaDoc();
150         String JavaDoc classBaseName = getBaseName( className ); // for inner classes
151
for( int i=0; i< bshmethods.length; i++ )
152             if ( bshmethods[i].getName().equals( classBaseName ) )
153                 consl.add( bshmethods[i] );
154             else
155                 methodsl.add( bshmethods[i] );
156
157         this.constructors = (DelayedEvalBshMethod [])consl.toArray(
158             new DelayedEvalBshMethod[0] );
159         this.methods = (DelayedEvalBshMethod [])methodsl.toArray(
160             new DelayedEvalBshMethod[0] );
161
162         try {
163             classStaticNameSpace.setLocalVariable(
164                 BSHCONSTRUCTORS, constructors, false/*strict*/ );
165         } catch ( UtilEvalError e ) {
166             throw new InterpreterError("can't set cons var");
167         }
168
169         this.isInterface = isInterface;
170     }
171
172     /**
173         Generate the class bytecode for this class.
174     */

175     public byte [] generateClass()
176     {
177         // Force the class public for now...
178
int classMods = getASMModifiers( classModifiers ) | ACC_PUBLIC;
179         if ( isInterface )
180             classMods |= ACC_INTERFACE;
181
182         String JavaDoc [] interfaceNames = new String JavaDoc [interfaces.length];
183         for(int i=0; i<interfaces.length; i++)
184             interfaceNames[i]=Type.getInternalName( interfaces[i] );
185
186         String JavaDoc sourceFile = "BeanShell Generated via ASM (www.objectweb.org)";
187         ClassWriter cw = new ClassWriter(false);
188         cw.visit( classMods, fqClassName, superClassName,
189             interfaceNames, sourceFile );
190
191         if ( !isInterface )
192         {
193             // Generate the bsh instance 'This' reference holder field
194
generateField(
195                 BSHTHIS+className, "Lbsh/This;", ACC_PUBLIC, cw);
196
197             // Generate the static bsh static reference holder field
198
generateField(
199                 BSHSTATIC+className, "Lbsh/This;", ACC_PUBLIC+ACC_STATIC, cw);
200         }
201
202         // Generate the fields
203
for( int i=0; i<vars.length; i++)
204         {
205             String JavaDoc type = vars[i].getTypeDescriptor();
206
207             // Don't generate private or loosely typed fields
208
// Note: loose types aren't currently parsed anyway...
209
if ( vars[i].hasModifier("private") || type == null )
210                 continue;
211         
212             int modifiers;
213             if ( isInterface )
214                 modifiers = ACC_PUBLIC | ACC_STATIC | ACC_FINAL;
215             else
216                 modifiers = getASMModifiers( vars[i].getModifiers() );
217
218             generateField( vars[i].getName(), type, modifiers , cw );
219         }
220
221         // Generate the constructors
222
boolean hasConstructor = false;
223         for( int i=0; i<constructors.length; i++)
224         {
225             // Don't generate private constructors
226
if ( constructors[i].hasModifier("private") )
227                 continue;
228
229             int modifiers = getASMModifiers( constructors[i].getModifiers() );
230             generateConstructor(
231                 i, constructors[i].getParamTypeDescriptors(), modifiers, cw );
232             hasConstructor = true;
233         }
234
235         // If no other constructors, generate a default constructor
236
if ( !isInterface && !hasConstructor )
237             generateConstructor(
238                 DEFAULTCONSTRUCTOR/*index*/, new String JavaDoc [0], ACC_PUBLIC, cw );
239
240         // Generate the delegate methods
241
for( int i=0; i<methods.length; i++)
242         {
243             String JavaDoc returnType = methods[i].getReturnTypeDescriptor();
244
245             // Don't generate private /*or loosely return typed */ methods
246
if ( methods[i].hasModifier("private") /*|| returnType == null*/ )
247                 continue;
248
249             int modifiers = getASMModifiers( methods[i].getModifiers() );
250             if ( isInterface )
251                 modifiers |= ( ACC_PUBLIC | ACC_ABSTRACT );
252
253             generateMethod( className, fqClassName,
254                 methods[i].getName(), returnType,
255                 methods[i].getParamTypeDescriptors(), modifiers, cw );
256
257             boolean isStatic = (modifiers & ACC_STATIC) > 0 ;
258             boolean overridden = classContainsMethod(
259                 superClass, methods[i].getName(),
260                 methods[i].getParamTypeDescriptors() ) ;
261             if ( !isStatic && overridden )
262                 generateSuperDelegateMethod( superClassName,
263                     methods[i].getName(), returnType,
264                     methods[i].getParamTypeDescriptors(), modifiers, cw );
265         }
266
267         return cw.toByteArray();
268     }
269
270     /**
271         Translate bsh.Modifiers into ASM modifier bitflags.
272     */

273     static int getASMModifiers( Modifiers modifiers )
274     {
275         int mods = 0;
276         if ( modifiers == null )
277             return mods;
278
279         if ( modifiers.hasModifier("public") )
280             mods += ACC_PUBLIC;
281         if ( modifiers.hasModifier("protected") )
282             mods += ACC_PROTECTED;
283         if ( modifiers.hasModifier("static") )
284             mods += ACC_STATIC;
285         if ( modifiers.hasModifier("synchronized") )
286             mods += ACC_SYNCHRONIZED;
287         if ( modifiers.hasModifier("abstract") )
288             mods += ACC_ABSTRACT;
289
290         return mods;
291     }
292
293     /**
294         Generate a field - static or instance.
295     */

296     static void generateField(
297         String JavaDoc fieldName, String JavaDoc type, int modifiers, ClassWriter cw )
298     {
299         cw.visitField( modifiers, fieldName, type, null/*value*/ );
300     }
301     
302     /**
303         Generate a delegate method - static or instance.
304         The generated code packs the method arguments into an object array
305         (wrapping primitive types in bsh.Primitive), invokes the static or
306         instance namespace invokeMethod() method, and then unwraps / returns
307         the result.
308     */

309     static void generateMethod(
310         String JavaDoc className, String JavaDoc fqClassName, String JavaDoc methodName,
311         String JavaDoc returnType, String JavaDoc[] paramTypes, int modifiers, ClassWriter cw )
312     {
313         String JavaDoc [] exceptions = null;
314         boolean isStatic = (modifiers & ACC_STATIC) != 0 ;
315
316         if ( returnType == null ) // map loose return type to Object
317
returnType = OBJECT;
318
319         String JavaDoc methodDescriptor = getMethodDescriptor( returnType, paramTypes );
320
321         // Generate method body
322
CodeVisitor cv = cw.visitMethod(
323             modifiers, methodName, methodDescriptor, exceptions );
324
325         if ( (modifiers & ACC_ABSTRACT) != 0 )
326             return;
327
328         // Generate code to push the BSHTHIS or BSHSTATIC field
329
if ( isStatic )
330         {
331             cv.visitFieldInsn(
332                 GETSTATIC, fqClassName, BSHSTATIC+className, "Lbsh/This;" );
333         }else
334         {
335             // Push 'this'
336
cv.visitVarInsn( ALOAD, 0 );
337
338             // Get the instance field
339
cv.visitFieldInsn(
340                 GETFIELD, fqClassName, BSHTHIS+className, "Lbsh/This;" );
341         }
342
343         // Push the name of the method as a constant
344
cv.visitLdcInsn( methodName );
345
346         // Generate code to push arguments as an object array
347
generateParameterReifierCode( paramTypes, isStatic, cv );
348
349         // Push nulls for various args of invokeMethod
350
cv.visitInsn(ACONST_NULL); // interpreter
351
cv.visitInsn(ACONST_NULL); // callstack
352
cv.visitInsn(ACONST_NULL); // callerinfo
353

354         // Push the boolean constant 'true' (for declaredOnly)
355
cv.visitInsn(ICONST_1);
356
357         // Invoke the method This.invokeMethod( name, Class [] sig, boolean )
358
cv.visitMethodInsn(
359             INVOKEVIRTUAL, "bsh/This", "invokeMethod",
360             Type.getMethodDescriptor(
361                 Type.getType(Object JavaDoc.class),
362                 new Type [] {
363                     Type.getType(String JavaDoc.class),
364                     Type.getType(Object JavaDoc [].class),
365                     Type.getType(Interpreter.class),
366                     Type.getType(CallStack.class),
367                     Type.getType(SimpleNode.class),
368                     Type.getType(Boolean.TYPE)
369                 }
370             )
371         );
372
373         // Generate code to unwrap bsh Primitive types
374
cv.visitMethodInsn(
375           INVOKESTATIC, "bsh/Primitive", "unwrap",
376           "(Ljava/lang/Object;)Ljava/lang/Object;" );
377
378         // Generate code to return the value
379
generateReturnCode( returnType, cv );
380
381         // Need to calculate this... just fudging here for now.
382
cv.visitMaxs( 20, 20 );
383     }
384
385     /**
386         Generate a constructor.
387     */

388     void generateConstructor(
389         int index, String JavaDoc [] paramTypes, int modifiers, ClassWriter cw )
390     {
391         /** offset after params of the args object [] var */
392         final int argsVar = paramTypes.length+1;
393         /** offset after params of the ConstructorArgs var */
394         final int consArgsVar = paramTypes.length+2;
395
396         String JavaDoc [] exceptions = null;
397         String JavaDoc methodDescriptor = getMethodDescriptor( "V", paramTypes );
398
399         // Create this constructor method
400
CodeVisitor cv =
401             cw.visitMethod( modifiers, "<init>", methodDescriptor, exceptions );
402
403         // Generate code to push arguments as an object array
404
generateParameterReifierCode( paramTypes, false/*isStatic*/, cv );
405         cv.visitVarInsn( ASTORE, argsVar );
406
407         // Generate the code implementing the alternate constructor switch
408
generateConstructorSwitch( index, argsVar, consArgsVar, cv );
409
410         // Generate code to invoke the ClassGeneratorUtil initInstance() method
411

412         // push 'this'
413
cv.visitVarInsn( ALOAD, 0 );
414
415         // Push the class/constructor name as a constant
416
cv.visitLdcInsn( className );
417
418         // Push arguments as an object array
419
cv.visitVarInsn( ALOAD, argsVar );
420
421         // invoke the initInstance() method
422
cv.visitMethodInsn(
423             INVOKESTATIC, "bsh/ClassGeneratorUtil", "initInstance",
424             "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V");
425
426         cv.visitInsn( RETURN );
427
428         // Need to calculate this... just fudging here for now.
429
cv.visitMaxs( 20, 20 );
430     }
431
432     /**
433         Generate a switch with a branch for each possible alternate
434         constructor. This includes all superclass constructors and all
435         constructors of this class. The default branch of this switch is the
436         default superclass constructor.
437         <p>
438         This method also generates the code to call the static
439         ClassGeneratorUtil
440         getConstructorArgs() method which inspects the scripted constructor to
441         find the alternate constructor signature (if any) and evalute the
442         arguments at runtime. The getConstructorArgs() method returns the
443         actual arguments as well as the index of the constructor to call.
444     */

445     void generateConstructorSwitch(
446         int consIndex, int argsVar, int consArgsVar, CodeVisitor cv )
447     {
448         Label defaultLabel = new Label();
449         Label endLabel = new Label();
450         int cases = superConstructors.length + constructors.length ;
451
452         Label [] labels = new Label[ cases ];
453         for(int i=0; i<cases; i++)
454             labels[i]=new Label();
455
456         // Generate code to call ClassGeneratorUtil to get our switch index
457
// and give us args...
458

459         // push super class name
460
cv.visitLdcInsn( superClass.getName() ); // use superClassName var?
461

462         // push class static This object
463
cv.visitFieldInsn(
464             GETSTATIC, fqClassName, BSHSTATIC+className, "Lbsh/This;" );
465
466         // push args
467
cv.visitVarInsn( ALOAD, argsVar );
468
469         // push this constructor index number onto stack
470
cv.visitIntInsn( BIPUSH, consIndex );
471
472         // invoke the ClassGeneratorUtil getConstructorsArgs() method
473
cv.visitMethodInsn(
474             INVOKESTATIC, "bsh/ClassGeneratorUtil", "getConstructorArgs",
475             "(Ljava/lang/String;Lbsh/This;[Ljava/lang/Object;I)"
476             +"Lbsh/ClassGeneratorUtil$ConstructorArgs;"
477         );
478
479         // store ConstructorArgs in consArgsVar
480
cv.visitVarInsn( ASTORE, consArgsVar );
481
482         // Get the ConstructorArgs selector field from ConstructorArgs
483

484         // push ConstructorArgs
485
cv.visitVarInsn( ALOAD, consArgsVar );
486         cv.visitFieldInsn(
487             GETFIELD, "bsh/ClassGeneratorUtil$ConstructorArgs", "selector", "I" );
488
489         // start switch
490
cv.visitTableSwitchInsn(
491             0/*min*/, cases-1/*max*/, defaultLabel, labels );
492
493         // generate switch body
494
int index = 0;
495         for( int i=0; i< superConstructors.length; i++, index++)
496             doSwitchBranch( index, superClassName,
497                 getTypeDescriptors( superConstructors[i].getParameterTypes() ),
498                 endLabel, labels, consArgsVar, cv );
499         for( int i=0; i< constructors.length; i++, index++)
500             doSwitchBranch( index, fqClassName,
501                 constructors[i].getParamTypeDescriptors(),
502                 endLabel, labels, consArgsVar, cv );
503     
504         // generate the default branch of switch
505
cv.visitLabel( defaultLabel );
506         // default branch always invokes no args super
507
cv.visitVarInsn( ALOAD, 0 ); // push 'this'
508
cv.visitMethodInsn( INVOKESPECIAL, superClassName, "<init>", "()V" );
509
510         // done with switch
511
cv.visitLabel( endLabel );
512     }
513
514     /*
515         Generate a branch of the constructor switch. This method is called by
516         generateConstructorSwitch.
517         The code generated by this method assumes that the argument array is
518         on the stack.
519     */

520     static void doSwitchBranch(
521         int index, String JavaDoc targetClassName, String JavaDoc [] paramTypes,
522         Label endLabel, Label [] labels, int consArgsVar, CodeVisitor cv
523     )
524     {
525         cv.visitLabel( labels[index] );
526         //cv.visitLineNumber( index, labels[index] );
527
cv.visitVarInsn( ALOAD, 0 ); // push this before args
528

529         // Unload the arguments from the ConstructorArgs object
530
for (int i=0; i<paramTypes.length; i++)
531         {
532             String JavaDoc type = paramTypes[i];
533             String JavaDoc method = null;
534             if ( type.equals("Z") )
535                 method = "getBoolean";
536             else if ( type.equals("B") )
537                 method = "getByte";
538             else if ( type.equals("C") )
539                 method = "getChar";
540             else if ( type.equals("S") )
541                 method = "getShort";
542             else if ( type.equals("I") )
543                 method = "getInt";
544             else if ( type.equals("J") )
545                 method = "getLong";
546             else if ( type.equals("D") )
547                 method = "getDouble";
548             else if ( type.equals("F") )
549                 method = "getFloat";
550             else
551                 method = "getObject";
552
553             // invoke the iterator method on the ConstructorArgs
554
cv.visitVarInsn( ALOAD, consArgsVar ); // push the ConstructorArgs
555
String JavaDoc className = "bsh/ClassGeneratorUtil$ConstructorArgs";
556             String JavaDoc retType;
557             if ( method.equals("getObject") )
558                 retType = OBJECT;
559             else
560                 retType = type;
561             cv.visitMethodInsn(INVOKEVIRTUAL, className, method, "()"+retType);
562             // if it's an object type we must do a check cast
563
if ( method.equals("getObject") )
564                 cv.visitTypeInsn( CHECKCAST, descriptorToClassName(type) );
565         }
566
567         // invoke the constructor for this branch
568
String JavaDoc descriptor = getMethodDescriptor( "V", paramTypes );
569         cv.visitMethodInsn(
570             INVOKESPECIAL, targetClassName, "<init>", descriptor );
571         cv.visitJumpInsn( GOTO, endLabel );
572     }
573     
574     static String JavaDoc getMethodDescriptor( String JavaDoc returnType, String JavaDoc [] paramTypes )
575     {
576         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("(");
577         for(int i=0; i<paramTypes.length; i++)
578             sb.append(paramTypes[i]);
579         sb.append(")"+returnType);
580         return sb.toString();
581     }
582
583     /**
584         Generate a superclass method delegate accessor method.
585         These methods are specially named methods which allow access to
586         overridden methods of the superclass (which the Java reflection API
587         normally does not allow).
588     */

589     // Maybe combine this with generateMethod()
590
static void generateSuperDelegateMethod( String JavaDoc superClassName, String JavaDoc methodName,
591                                              String JavaDoc returnType, String JavaDoc[] paramTypes, int modifiers, ClassWriter cw )
592     {
593         String JavaDoc [] exceptions = null;
594
595         if ( returnType == null ) // map loose return to Object
596
returnType = OBJECT;
597
598         String JavaDoc methodDescriptor = getMethodDescriptor( returnType, paramTypes );
599
600         // Add method body
601
CodeVisitor cv = cw.visitMethod(
602             modifiers, "_bshSuper"+methodName, methodDescriptor, exceptions );
603
604         cv.visitVarInsn(ALOAD, 0);
605         // Push vars
606
int localVarIndex = 1;
607         for (int i = 0; i < paramTypes.length; ++i)
608         {
609             if ( isPrimitive( paramTypes[i]) )
610                 cv.visitVarInsn(ILOAD, localVarIndex);
611             else
612                 cv.visitVarInsn(ALOAD, localVarIndex);
613             localVarIndex +=
614                 ( (paramTypes[i].equals("D") || paramTypes[i].equals("J"))
615                     ? 2 : 1 );
616         }
617
618         cv.visitMethodInsn( INVOKESPECIAL,
619             superClassName, methodName, methodDescriptor );
620
621         generatePlainReturnCode( returnType, cv );
622
623         // Need to calculate this... just fudging here for now.
624
cv.visitMaxs( 20, 20 );
625     }
626
627     boolean classContainsMethod(
628         Class JavaDoc clas, String JavaDoc methodName, String JavaDoc [] paramTypes )
629     {
630         while( clas != null )
631         {
632             Method [] methods = clas.getDeclaredMethods();
633             for( int i =0; i<methods.length; i++ )
634             {
635                 if ( methods[i].getName().equals(methodName) )
636                 {
637                     String JavaDoc [] methodParamTypes =
638                         getTypeDescriptors( methods[i].getParameterTypes() );
639                     boolean found = true;
640                     for( int j=0; j<methodParamTypes.length; j++)
641                     {
642                         if ( ! paramTypes[j].equals( methodParamTypes[j] ) ) {
643                             found = false;
644                             break;
645                         }
646                     }
647                     if ( found )
648                         return true;
649                 }
650             }
651
652             clas = clas.getSuperclass();
653         }
654
655         return false;
656     }
657
658     /**
659         Generate return code for a normal bytecode
660     */

661     static void generatePlainReturnCode( String JavaDoc returnType, CodeVisitor cv )
662     {
663         if ( returnType.equals("V") )
664             cv.visitInsn( RETURN );
665         else
666         if ( isPrimitive( returnType ) )
667         {
668             int opcode = IRETURN;
669             if ( returnType.equals("D") )
670                 opcode = DRETURN;
671             else if ( returnType.equals("F") )
672                 opcode = FRETURN;
673             else if ( returnType.equals("J") ) //long
674
opcode = LRETURN;
675
676             cv.visitInsn(opcode);
677         }
678         else {
679             cv.visitTypeInsn( CHECKCAST, descriptorToClassName(returnType) );
680             cv.visitInsn( ARETURN );
681         }
682     }
683
684     /**
685         Generates the code to reify the arguments of the given method.
686         For a method "int m (int i, String s)", this code is the bytecode
687         corresponding to the "new Object[] { new bsh.Primitive(i), s }"
688         expression.
689
690         @author Eric Bruneton
691         @author Pat Niemeyer
692         @param cv the code visitor to be used to generate the bytecode.
693         @param isStatic the enclosing methods is static
694     */

695     public static void generateParameterReifierCode (
696         String JavaDoc [] paramTypes, boolean isStatic, final CodeVisitor cv )
697     {
698         cv.visitIntInsn(SIPUSH, paramTypes.length);
699         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
700         int localVarIndex = isStatic ? 0 : 1;
701         for (int i = 0; i < paramTypes.length; ++i)
702         {
703             String JavaDoc param = paramTypes[i];
704             cv.visitInsn(DUP);
705             cv.visitIntInsn(SIPUSH, i);
706             if ( isPrimitive( param ) )
707             {
708                 int opcode;
709                 if (param.equals("F")) {
710                     opcode = FLOAD;
711                 } else if (param.equals("D")) {
712                     opcode = DLOAD;
713                 } else if (param.equals("J")) {
714                     opcode = LLOAD;
715                 } else {
716                     opcode = ILOAD;
717                 }
718
719                 String JavaDoc type = "bsh/Primitive";
720                 cv.visitTypeInsn( NEW, type );
721                 cv.visitInsn(DUP);
722                 cv.visitVarInsn(opcode, localVarIndex);
723                 String JavaDoc desc = param; // ok?
724
cv.visitMethodInsn(
725                     INVOKESPECIAL, type, "<init>", "(" + desc + ")V");
726             } else {
727                 // Technically incorrect here - we need to wrap null values
728
// as bsh.Primitive.NULL. However the This.invokeMethod()
729
// will do that much for us.
730
// We need to generate a conditional here to test for null
731
// and return Primitive.NULL
732
cv.visitVarInsn( ALOAD, localVarIndex );
733             }
734             cv.visitInsn(AASTORE);
735             localVarIndex +=
736                 ( (param.equals("D") || param.equals("J")) ? 2 : 1 );
737         }
738   }
739
740   /**
741         Generates the code to unreify the result of the given method. For a
742         method "int m (int i, String s)", this code is the bytecode
743         corresponding to the "((Integer)...).intValue()" expression.
744        
745         @param cv the code visitor to be used to generate the bytecode.
746         @author Eric Bruneton
747         @author Pat Niemeyer
748    */

749     public static void generateReturnCode (
750         String JavaDoc returnType, CodeVisitor cv )
751     {
752         if ( returnType.equals("V") )
753         {
754             cv.visitInsn(POP);
755             cv.visitInsn(RETURN);
756         }
757         else if ( isPrimitive( returnType ) )
758         {
759             int opcode = IRETURN;
760             String JavaDoc type;
761             String JavaDoc meth;
762             if ( returnType.equals("B") ) {
763                 type = "java/lang/Byte";
764                 meth = "byteValue";
765             } else if (returnType.equals("I") ) {
766                 type = "java/lang/Integer";
767                 meth = "intValue";
768             } else if (returnType.equals("Z") ) {
769                 type = "java/lang/Boolean";
770                 meth = "booleanValue";
771             } else if (returnType.equals("D") ) {
772                 opcode = DRETURN;
773                 type = "java/lang/Double";
774                 meth = "doubleValue";
775             } else if (returnType.equals("F") ) {
776                 opcode = FRETURN;
777                 type = "java/lang/Float";
778                 meth = "floatValue";
779             } else if (returnType.equals("J") ) {
780                 opcode = LRETURN;
781                 type = "java/lang/Long";
782                 meth = "longValue";
783             } else if (returnType.equals("C") ) {
784                 type = "java/lang/Character";
785                 meth = "charValue";
786             } else /*if (returnType.equals("S") )*/ {
787                 type = "java/lang/Short";
788                 meth = "shortValue";
789             }
790
791             String JavaDoc desc = returnType;
792             cv.visitTypeInsn( CHECKCAST, type ); // type is correct here
793
cv.visitMethodInsn( INVOKEVIRTUAL, type, meth, "()" + desc );
794             cv.visitInsn(opcode);
795         } else
796         {
797             cv.visitTypeInsn( CHECKCAST, descriptorToClassName(returnType) );
798             cv.visitInsn(ARETURN);
799         }
800   }
801
802     /**
803         Evaluate the arguments (if any) for the constructor specified by
804         the constructor index. Return the ConstructorArgs object which
805         contains the actual arguments to the alternate constructor and also the
806         index of that constructor for the constructor switch.
807
808         @param consArgs the arguments to the constructor. These are necessary in
809         the evaluation of the alt constructor args. e.g. Foo(a) { super(a); }
810         @return the ConstructorArgs object containing a constructor selector
811             and evaluated arguments for the alternate constructor
812     */

813     public static ConstructorArgs getConstructorArgs(
814         String JavaDoc superClassName, This classStaticThis,
815         Object JavaDoc [] consArgs, int index )
816     {
817         DelayedEvalBshMethod [] constructors;
818         try {
819             constructors =
820                 (DelayedEvalBshMethod [])classStaticThis.getNameSpace()
821                 .getVariable( BSHCONSTRUCTORS );
822         } catch ( Exception JavaDoc e ) {
823             throw new InterpreterError(
824                 "unable to get instance initializer: "+e );
825         }
826
827         if ( index == DEFAULTCONSTRUCTOR ) // auto-gen default constructor
828
return ConstructorArgs.DEFAULT; // use default super constructor
829

830         DelayedEvalBshMethod constructor = constructors[index];
831
832         if ( constructor.methodBody.jjtGetNumChildren() == 0 )
833             return ConstructorArgs.DEFAULT; // use default super constructor
834

835         // Determine if the constructor calls this() or super()
836
String JavaDoc altConstructor = null;
837         BSHArguments argsNode = null;
838         SimpleNode firstStatement =
839             (SimpleNode)constructor.methodBody.jjtGetChild(0);
840         if ( firstStatement instanceof BSHPrimaryExpression )
841             firstStatement = (SimpleNode)firstStatement.jjtGetChild(0);
842         if ( firstStatement instanceof BSHMethodInvocation )
843         {
844             BSHMethodInvocation methodNode =
845                 (BSHMethodInvocation)firstStatement;
846             BSHAmbiguousName methodName = methodNode.getNameNode();
847             if ( methodName.text.equals("super")
848                 || methodName.text.equals("this")
849             ) {
850                 altConstructor = methodName.text;
851                 argsNode = methodNode.getArgsNode();
852             }
853         }
854
855         if ( altConstructor == null )
856             return ConstructorArgs.DEFAULT; // use default super constructor
857

858         // Make a tmp namespace to hold the original constructor args for
859
// use in eval of the parameters node
860
NameSpace consArgsNameSpace =
861             new NameSpace( classStaticThis.getNameSpace(), "consArgs" );
862         String JavaDoc [] consArgNames = constructor.getParameterNames();
863         Class JavaDoc [] consArgTypes = constructor.getParameterTypes();
864         for( int i=0; i<consArgs.length; i++ )
865         {
866             try {
867                 consArgsNameSpace.setTypedVariable(
868                     consArgNames[i], consArgTypes[i], consArgs[i],
869                     null/*modifiers*/);
870             } catch ( UtilEvalError e ) {
871                 throw new InterpreterError("err setting local cons arg:"+e);
872             }
873         }
874
875         // evaluate the args
876

877         CallStack callstack = new CallStack();
878         callstack.push( consArgsNameSpace);
879         Object JavaDoc [] args = null;
880         Interpreter interpreter = classStaticThis.declaringInterpreter;
881
882         try {
883             args = argsNode.getArguments( callstack, interpreter );
884         } catch ( EvalError e ) {
885             throw new InterpreterError(
886                 "Error evaluating constructor args: "+e );
887         }
888
889         Class JavaDoc [] argTypes = Types.getTypes( args );
890         args = Primitive.unwrap( args );
891         Class JavaDoc superClass =
892             interpreter.getClassManager().classForName( superClassName );
893         if ( superClass == null )
894             throw new InterpreterError(
895                 "can't find superclass: "+superClassName );
896         Constructor [] superCons = superClass.getDeclaredConstructors();
897
898         // find the matching super() constructor for the args
899
if ( altConstructor.equals("super") )
900         {
901             int i = Reflect.findMostSpecificConstructorIndex(
902                 argTypes , superCons );
903             if ( i == -1 )
904                 throw new InterpreterError("can't find constructor for args!");
905             return new ConstructorArgs( i, args );
906         }
907
908         // find the matching this() constructor for the args
909
Class JavaDoc [][] candidates = new Class JavaDoc [ constructors.length ] [];
910         for(int i=0; i< candidates.length; i++ )
911             candidates[i] = constructors[i].getParameterTypes();
912         int i = Reflect.findMostSpecificSignature( argTypes, candidates );
913         if ( i == -1 )
914             throw new InterpreterError("can't find constructor for args 2!");
915         // this() constructors come after super constructors in the table
916

917         int selector = i+superCons.length;
918         int ourSelector = index+superCons.length;
919
920         // Are we choosing ourselves recursively through a this() reference?
921
if ( selector == ourSelector )
922             throw new InterpreterError( "Recusive constructor call.");
923
924         return new ConstructorArgs( selector, args );
925     }
926     
927     /**
928         Initialize an instance of the class.
929         This method is called from the generated class constructor to evaluate
930         the instance initializer and scripted constructor in the instance
931         namespace.
932     */

933     public static void initInstance(
934         Object JavaDoc instance, String JavaDoc className, Object JavaDoc [] args )
935     {
936         Class JavaDoc [] sig = Types.getTypes( args );
937         CallStack callstack = new CallStack();
938         Interpreter interpreter;
939         NameSpace instanceNameSpace;
940
941         // check to see if the instance has already been initialized
942
// (the case if using a this() alternate constuctor)
943
This instanceThis = getClassInstanceThis( instance, className );
944
945 // XXX clean up this conditional
946
if ( instanceThis == null )
947         {
948             // Create the instance 'This' namespace, set it on the object
949
// instance and invoke the instance initializer
950

951             // Get the static This reference from the proto-instance
952
This classStaticThis =
953                 getClassStaticThis( instance.getClass(), className );
954             interpreter = classStaticThis.declaringInterpreter;
955
956             // Get the instance initializer block from the static This
957
BSHBlock instanceInitBlock;
958             try {
959                 instanceInitBlock = (BSHBlock)classStaticThis.getNameSpace()
960                     .getVariable( BSHINIT );
961             } catch ( Exception JavaDoc e ) {
962                 throw new InterpreterError(
963                     "unable to get instance initializer: "+e );
964             }
965
966             // Create the instance namespace
967
instanceNameSpace =
968                 new NameSpace( classStaticThis.getNameSpace(), className );
969             instanceNameSpace.isClass = true;
970
971             // Set the instance This reference on the instance
972
instanceThis = instanceNameSpace.getThis( interpreter );
973             try {
974                 LHS lhs =
975                     Reflect.getLHSObjectField( instance, BSHTHIS+className );
976                 lhs.assign( instanceThis, false/*strict*/ );
977             } catch ( Exception JavaDoc e ) {
978                 throw new InterpreterError("Error in class gen setup: "+e );
979             }
980
981             // Give the instance space its object import
982
instanceNameSpace.setClassInstance( instance );
983
984             // should use try/finally here to pop ns
985
callstack.push( instanceNameSpace );
986
987             // evaluate the instance portion of the block in it
988
try { // Evaluate the initializer block
989
instanceInitBlock.evalBlock(
990                     callstack, interpreter, true/*override*/,
991                     ClassGeneratorImpl.ClassNodeFilter.CLASSINSTANCE );
992             } catch ( Exception JavaDoc e ) {
993                 throw new InterpreterError("Error in class initialization: "+e);
994             }
995
996             callstack.pop();
997
998         } else
999         {
1000            // The object instance has already been initialzed by another
1001
// constructor. Fall through to invoke the constructor body below.
1002
interpreter = instanceThis.declaringInterpreter;
1003            instanceNameSpace = instanceThis.getNameSpace();
1004        }
1005
1006        // invoke the constructor method from the instanceThis
1007

1008        String JavaDoc constructorName = getBaseName( className );
1009        try {
1010            // Find the constructor (now in the instance namespace)
1011
BshMethod constructor = instanceNameSpace.getMethod(
1012                constructorName, sig, true/*declaredOnly*/ );
1013
1014            // if args, we must have constructor
1015
if ( args.length > 0 && constructor == null )
1016                throw new InterpreterError(
1017                    "Can't find constructor: "+ className );
1018
1019            // Evaluate the constructor
1020
if ( constructor != null )
1021                constructor.invoke( args, interpreter, callstack,
1022                    null/*callerInfo*/, false/*overrideNameSpace*/ ) ;
1023        } catch ( Exception JavaDoc e ) {
1024            if ( e instanceof TargetError )
1025                e =(Exception JavaDoc)((TargetError)e).getTarget();
1026            if ( e instanceof InvocationTargetException )
1027                e = (Exception JavaDoc)((InvocationTargetException)e)
1028                    .getTargetException();
1029            e.printStackTrace( System.err );
1030            throw new InterpreterError("Error in class initialization: "+e );
1031        }
1032    }
1033
1034    /**
1035        Get the static bsh namespace field from the class.
1036        @param className may be the name of clas itself or a superclass of clas.
1037    */

1038    static This getClassStaticThis( Class JavaDoc clas, String JavaDoc className )
1039    {
1040        try {
1041            return (This)Reflect.getStaticFieldValue(
1042                clas, BSHSTATIC + className );
1043        } catch ( Exception JavaDoc e ) {
1044            throw new InterpreterError("Unable to get class static space: "+e);
1045        }
1046    }
1047
1048    /**
1049        Get the instance bsh namespace field from the object instance.
1050        @return the class instance This object or null if the object has not
1051        been initialized.
1052    */

1053    static This getClassInstanceThis( Object JavaDoc instance, String JavaDoc className )
1054    {
1055        try {
1056            Object JavaDoc o = Reflect.getObjectFieldValue( instance, BSHTHIS+className );
1057            return (This)Primitive.unwrap(o); // unwrap Primitive.Null to null
1058
} catch ( Exception JavaDoc e ) {
1059            throw new InterpreterError(
1060                "Generated class: Error getting This"+e );
1061        }
1062    }
1063
1064    /**
1065        Does the type descriptor string describe a primitive type?
1066    */

1067    private static boolean isPrimitive( String JavaDoc typeDescriptor )
1068    {
1069        return typeDescriptor.length() == 1; // right?
1070
}
1071
1072    static String JavaDoc[] getTypeDescriptors( Class JavaDoc [] cparams )
1073    {
1074        String JavaDoc [] sa = new String JavaDoc [cparams.length];
1075        for(int i=0; i<sa.length; i++)
1076            sa[i] = BSHType.getTypeDescriptor( cparams[i] );
1077        return sa;
1078    }
1079
1080    /**
1081        If a non-array object type, remove the prefix "L" and suffix ";".
1082    */

1083    // Can this be factored out...?
1084
// Should be be adding the L...; here instead?
1085
private static String JavaDoc descriptorToClassName( String JavaDoc s )
1086    {
1087        if ( s.startsWith("[") || !s.startsWith("L") )
1088            return s;
1089        return s.substring( 1, s.length()-1 );
1090    }
1091
1092    private static String JavaDoc getBaseName( String JavaDoc className )
1093    {
1094        int i = className.indexOf("$");
1095        if ( i == -1 )
1096            return className;
1097
1098        return className.substring(i+1);
1099    }
1100
1101    /**
1102        A ConstructorArgs object holds evaluated arguments for a constructor
1103        call as well as the index of a possible alternate selector to invoke.
1104        This object is used by the constructor switch.
1105        @see #generateConstructor( int , String [] , int , ClassWriter )
1106    */

1107    public static class ConstructorArgs
1108    {
1109        /** A ConstructorArgs which calls the default constructor */
1110        public static ConstructorArgs DEFAULT = new ConstructorArgs();
1111
1112        public int selector = DEFAULTCONSTRUCTOR;
1113        Object JavaDoc [] args;
1114        int arg = 0;
1115        /**
1116            The index of the constructor to call.
1117        */

1118
1119        ConstructorArgs() { }
1120
1121        ConstructorArgs( int selector, Object JavaDoc [] args ) {
1122            this.selector = selector;
1123            this.args = args;
1124        }
1125
1126        Object JavaDoc next() { return args[arg++]; }
1127
1128        public boolean getBoolean() { return ((Boolean JavaDoc)next()).booleanValue(); }
1129        public byte getByte() { return ((Byte JavaDoc)next()).byteValue(); }
1130        public char getChar() { return ((Character JavaDoc)next()).charValue(); }
1131        public short getShort() { return ((Short JavaDoc)next()).shortValue(); }
1132        public int getInt() { return ((Integer JavaDoc)next()).intValue(); }
1133        public long getLong() { return ((Long JavaDoc)next()).longValue(); }
1134        public double getDouble() { return ((Double JavaDoc)next()).doubleValue(); }
1135        public float getFloat() { return ((Float JavaDoc)next()).floatValue(); }
1136        public Object JavaDoc getObject() { return next(); }
1137    }
1138}
1139
Popular Tags