PageRenderTime 32ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/bsh/ClassGeneratorUtil.java

#
Java | 1138 lines | 707 code | 147 blank | 284 comment | 145 complexity | 9b755766240ebaac6ff37597a670ea2d MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  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. package org.gjt.sp.jedit.bsh;
  34. import org.gjt.sp.jedit.bsh.org.objectweb.asm.*;
  35. import org.gjt.sp.jedit.bsh.org.objectweb.asm.Type;
  36. import java.lang.reflect.*;
  37. import java.util.ArrayList;
  38. import java.util.List;
  39. /**
  40. ClassGeneratorUtil utilizes the ASM (www.objectweb.org) bytecode generator
  41. by Eric Bruneton in order to generate class "stubs" for BeanShell at
  42. runtime.
  43. <p>
  44. Stub classes contain all of the fields of a BeanShell scripted class
  45. as well as two "callback" references to BeanShell namespaces: one for
  46. static methods and one for instance methods. Methods of the class are
  47. delegators which invoke corresponding methods on either the static or
  48. instance bsh object and then unpack and return the results. The static
  49. namespace utilizes a static import to delegate variable access to the
  50. class' static fields. The instance namespace utilizes a dynamic import
  51. (i.e. mixin) to delegate variable access to the class' instance variables.
  52. <p>
  53. Constructors for the class delegate to the static initInstance() method of
  54. ClassGeneratorUtil to initialize new instances of the object. initInstance()
  55. invokes the instance intializer code (init vars and instance blocks) and
  56. then delegates to the corresponding scripted constructor method in the
  57. instance namespace. Constructors contain special switch logic which allows
  58. the BeanShell to control the calling of alternate constructors (this() or
  59. super() references) at runtime.
  60. <p>
  61. Specially named superclass delegator methods are also generated in order to
  62. allow BeanShell to access overridden methods of the superclass (which
  63. reflection does not normally allow).
  64. <p>
  65. @author Pat Niemeyer
  66. */
  67. /*
  68. Notes:
  69. It would not be hard to eliminate the use of org.objectweb.asm.Type from
  70. this class, making the distribution a tiny bit smaller.
  71. */
  72. public class ClassGeneratorUtil implements Constants
  73. {
  74. /** The name of the static field holding the reference to the bsh
  75. static This (the callback namespace for static methods) */
  76. static final String BSHSTATIC="_bshStatic";
  77. /** The name of the instance field holding the reference to the bsh
  78. instance This (the callback namespace for instance methods) */
  79. static final String BSHTHIS="_bshThis";
  80. /** The prefix for the name of the super delegate methods. e.g.
  81. _bshSuperfoo() is equivalent to super.foo() */
  82. static final String BSHSUPER="_bshSuper";
  83. /** The bsh static namespace variable name of the instance initializer */
  84. static final String BSHINIT="_bshInstanceInitializer";
  85. /** The bsh static namespace variable that holds the constructor methods */
  86. static final String BSHCONSTRUCTORS="_bshConstructors";
  87. /** The switch branch number for the default constructor.
  88. The value -1 will cause the default branch to be taken. */
  89. static final int DEFAULTCONSTRUCTOR = -1;
  90. static final String OBJECT= "Ljava/lang/Object;";
  91. String className;
  92. /** fully qualified class name (with package) e.g. foo/bar/Blah */
  93. String fqClassName;
  94. Class superClass;
  95. String superClassName;
  96. Class [] interfaces;
  97. Variable [] vars;
  98. Constructor [] superConstructors;
  99. DelayedEvalBshMethod [] constructors;
  100. DelayedEvalBshMethod [] methods;
  101. NameSpace classStaticNameSpace;
  102. Modifiers classModifiers;
  103. boolean isInterface;
  104. /**
  105. @param packageName e.g. "com.foo.bar"
  106. */
  107. public ClassGeneratorUtil(
  108. Modifiers classModifiers, String className, String packageName,
  109. Class superClass, Class [] interfaces, Variable [] vars,
  110. DelayedEvalBshMethod [] bshmethods, NameSpace classStaticNameSpace,
  111. boolean isInterface
  112. )
  113. {
  114. this.classModifiers = classModifiers;
  115. this.className = className;
  116. if ( packageName != null )
  117. this.fqClassName = packageName.replace('.','/') + "/" + className;
  118. else
  119. this.fqClassName = className;
  120. if ( superClass == null )
  121. superClass = Object.class;
  122. this.superClass = superClass;
  123. this.superClassName = Type.getInternalName( superClass );
  124. if ( interfaces == null )
  125. interfaces = new Class[0];
  126. this.interfaces = interfaces;
  127. this.vars = vars;
  128. this.classStaticNameSpace = classStaticNameSpace;
  129. this.superConstructors = superClass.getDeclaredConstructors();
  130. // Split the methods into constructors and regular method lists
  131. List consl = new ArrayList();
  132. List methodsl = new ArrayList();
  133. String classBaseName = getBaseName( className ); // for inner classes
  134. for( int i=0; i< bshmethods.length; i++ )
  135. if ( bshmethods[i].getName().equals( classBaseName ) )
  136. consl.add( bshmethods[i] );
  137. else
  138. methodsl.add( bshmethods[i] );
  139. this.constructors = (DelayedEvalBshMethod [])consl.toArray(
  140. new DelayedEvalBshMethod[0] );
  141. this.methods = (DelayedEvalBshMethod [])methodsl.toArray(
  142. new DelayedEvalBshMethod[0] );
  143. try {
  144. classStaticNameSpace.setLocalVariable(
  145. BSHCONSTRUCTORS, constructors, false/*strict*/ );
  146. } catch ( UtilEvalError e ) {
  147. throw new InterpreterError("can't set cons var");
  148. }
  149. this.isInterface = isInterface;
  150. }
  151. /**
  152. Generate the class bytecode for this class.
  153. */
  154. public byte [] generateClass()
  155. {
  156. // Force the class public for now...
  157. int classMods = getASMModifiers( classModifiers ) | ACC_PUBLIC;
  158. if ( isInterface )
  159. classMods |= ACC_INTERFACE;
  160. String [] interfaceNames = new String [interfaces.length];
  161. for(int i=0; i<interfaces.length; i++)
  162. interfaceNames[i]=Type.getInternalName( interfaces[i] );
  163. String sourceFile = "BeanShell Generated via ASM (www.objectweb.org)";
  164. ClassWriter cw = new ClassWriter(false);
  165. cw.visit( classMods, fqClassName, superClassName,
  166. interfaceNames, sourceFile );
  167. if ( !isInterface )
  168. {
  169. // Generate the bsh instance 'This' reference holder field
  170. generateField(
  171. BSHTHIS+className, "Lorg/gjt/sp/jedit/bsh/This;", ACC_PUBLIC, cw);
  172. // Generate the static bsh static reference holder field
  173. generateField(
  174. BSHSTATIC+className, "Lorg/gjt/sp/jedit/bsh/This;", ACC_PUBLIC+ACC_STATIC, cw);
  175. }
  176. // Generate the fields
  177. for( int i=0; i<vars.length; i++)
  178. {
  179. String type = vars[i].getTypeDescriptor();
  180. // Don't generate private or loosely typed fields
  181. // Note: loose types aren't currently parsed anyway...
  182. if ( vars[i].hasModifier("private") || type == null )
  183. continue;
  184. int modifiers;
  185. if ( isInterface )
  186. modifiers = ACC_PUBLIC | ACC_STATIC | ACC_FINAL;
  187. else
  188. modifiers = getASMModifiers( vars[i].getModifiers() );
  189. generateField( vars[i].getName(), type, modifiers , cw );
  190. }
  191. // Generate the constructors
  192. boolean hasConstructor = false;
  193. for( int i=0; i<constructors.length; i++)
  194. {
  195. // Don't generate private constructors
  196. if ( constructors[i].hasModifier("private") )
  197. continue;
  198. int modifiers = getASMModifiers( constructors[i].getModifiers() );
  199. generateConstructor(
  200. i, constructors[i].getParamTypeDescriptors(), modifiers, cw );
  201. hasConstructor = true;
  202. }
  203. // If no other constructors, generate a default constructor
  204. if ( !isInterface && !hasConstructor )
  205. generateConstructor(
  206. DEFAULTCONSTRUCTOR/*index*/, new String [0], ACC_PUBLIC, cw );
  207. // Generate the delegate methods
  208. for( int i=0; i<methods.length; i++)
  209. {
  210. String returnType = methods[i].getReturnTypeDescriptor();
  211. // Don't generate private /*or loosely return typed */ methods
  212. if ( methods[i].hasModifier("private") /*|| returnType == null*/ )
  213. continue;
  214. int modifiers = getASMModifiers( methods[i].getModifiers() );
  215. if ( isInterface )
  216. modifiers |= ( ACC_PUBLIC | ACC_ABSTRACT );
  217. generateMethod( className, fqClassName,
  218. methods[i].getName(), returnType,
  219. methods[i].getParamTypeDescriptors(), modifiers, cw );
  220. boolean isStatic = (modifiers & ACC_STATIC) > 0 ;
  221. boolean overridden = classContainsMethod(
  222. superClass, methods[i].getName(),
  223. methods[i].getParamTypeDescriptors() ) ;
  224. if ( !isStatic && overridden )
  225. generateSuperDelegateMethod( superClassName,
  226. methods[i].getName(), returnType,
  227. methods[i].getParamTypeDescriptors(), modifiers, cw );
  228. }
  229. return cw.toByteArray();
  230. }
  231. /**
  232. Translate bsh.Modifiers into ASM modifier bitflags.
  233. */
  234. static int getASMModifiers( Modifiers modifiers )
  235. {
  236. int mods = 0;
  237. if ( modifiers == null )
  238. return mods;
  239. if ( modifiers.hasModifier("public") )
  240. mods += ACC_PUBLIC;
  241. if ( modifiers.hasModifier("protected") )
  242. mods += ACC_PROTECTED;
  243. if ( modifiers.hasModifier("static") )
  244. mods += ACC_STATIC;
  245. if ( modifiers.hasModifier("synchronized") )
  246. mods += ACC_SYNCHRONIZED;
  247. if ( modifiers.hasModifier("abstract") )
  248. mods += ACC_ABSTRACT;
  249. return mods;
  250. }
  251. /**
  252. Generate a field - static or instance.
  253. */
  254. static void generateField(
  255. String fieldName, String type, int modifiers, ClassWriter cw )
  256. {
  257. cw.visitField( modifiers, fieldName, type, null/*value*/ );
  258. }
  259. /**
  260. Generate a delegate method - static or instance.
  261. The generated code packs the method arguments into an object array
  262. (wrapping primitive types in bsh.Primitive), invokes the static or
  263. instance namespace invokeMethod() method, and then unwraps / returns
  264. the result.
  265. */
  266. static void generateMethod(
  267. String className, String fqClassName, String methodName,
  268. String returnType, String[] paramTypes, int modifiers, ClassWriter cw )
  269. {
  270. String [] exceptions = null;
  271. boolean isStatic = (modifiers & ACC_STATIC) != 0 ;
  272. if ( returnType == null ) // map loose return type to Object
  273. returnType = OBJECT;
  274. String methodDescriptor = getMethodDescriptor( returnType, paramTypes );
  275. // Generate method body
  276. CodeVisitor cv = cw.visitMethod(
  277. modifiers, methodName, methodDescriptor, exceptions );
  278. if ( (modifiers & ACC_ABSTRACT) != 0 )
  279. return;
  280. // Generate code to push the BSHTHIS or BSHSTATIC field
  281. if ( isStatic )
  282. {
  283. cv.visitFieldInsn(
  284. GETSTATIC, fqClassName, BSHSTATIC+className, "Lorg/gjt/sp/jedit/bsh/This;" );
  285. }else
  286. {
  287. // Push 'this'
  288. cv.visitVarInsn( ALOAD, 0 );
  289. // Get the instance field
  290. cv.visitFieldInsn(
  291. GETFIELD, fqClassName, BSHTHIS+className, "Lorg/gjt/sp/jedit/bsh/This;" );
  292. }
  293. // Push the name of the method as a constant
  294. cv.visitLdcInsn( methodName );
  295. // Generate code to push arguments as an object array
  296. generateParameterReifierCode( paramTypes, isStatic, cv );
  297. // Push nulls for various args of invokeMethod
  298. cv.visitInsn(ACONST_NULL); // interpreter
  299. cv.visitInsn(ACONST_NULL); // callstack
  300. cv.visitInsn(ACONST_NULL); // callerinfo
  301. // Push the boolean constant 'true' (for declaredOnly)
  302. cv.visitInsn(ICONST_1);
  303. // Invoke the method This.invokeMethod( name, Class [] sig, boolean )
  304. cv.visitMethodInsn(
  305. INVOKEVIRTUAL, "org/gjt/sp/jedit/bsh/This", "invokeMethod",
  306. Type.getMethodDescriptor(
  307. Type.getType(Object.class),
  308. new Type [] {
  309. Type.getType(String.class),
  310. Type.getType(Object [].class),
  311. Type.getType(Interpreter.class),
  312. Type.getType(CallStack.class),
  313. Type.getType(SimpleNode.class),
  314. Type.getType(Boolean.TYPE)
  315. }
  316. )
  317. );
  318. // Generate code to unwrap bsh Primitive types
  319. cv.visitMethodInsn(
  320. INVOKESTATIC, "org/gjt/sp/jedit/bsh/Primitive", "unwrap",
  321. "(Ljava/lang/Object;)Ljava/lang/Object;" );
  322. // Generate code to return the value
  323. generateReturnCode( returnType, cv );
  324. // Need to calculate this... just fudging here for now.
  325. cv.visitMaxs( 20, 20 );
  326. }
  327. /**
  328. Generate a constructor.
  329. */
  330. void generateConstructor(
  331. int index, String [] paramTypes, int modifiers, ClassWriter cw )
  332. {
  333. /** offset after params of the args object [] var */
  334. final int argsVar = paramTypes.length+1;
  335. /** offset after params of the ConstructorArgs var */
  336. final int consArgsVar = paramTypes.length+2;
  337. String [] exceptions = null;
  338. String methodDescriptor = getMethodDescriptor( "V", paramTypes );
  339. // Create this constructor method
  340. CodeVisitor cv =
  341. cw.visitMethod( modifiers, "<init>", methodDescriptor, exceptions );
  342. // Generate code to push arguments as an object array
  343. generateParameterReifierCode( paramTypes, false/*isStatic*/, cv );
  344. cv.visitVarInsn( ASTORE, argsVar );
  345. // Generate the code implementing the alternate constructor switch
  346. generateConstructorSwitch( index, argsVar, consArgsVar, cv );
  347. // Generate code to invoke the ClassGeneratorUtil initInstance() method
  348. // push 'this'
  349. cv.visitVarInsn( ALOAD, 0 );
  350. // Push the class/constructor name as a constant
  351. cv.visitLdcInsn( className );
  352. // Push arguments as an object array
  353. cv.visitVarInsn( ALOAD, argsVar );
  354. // invoke the initInstance() method
  355. cv.visitMethodInsn(
  356. INVOKESTATIC, "org/gjt/sp/jedit/bsh/ClassGeneratorUtil", "initInstance",
  357. "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V");
  358. cv.visitInsn( RETURN );
  359. // Need to calculate this... just fudging here for now.
  360. cv.visitMaxs( 20, 20 );
  361. }
  362. /**
  363. Generate a switch with a branch for each possible alternate
  364. constructor. This includes all superclass constructors and all
  365. constructors of this class. The default branch of this switch is the
  366. default superclass constructor.
  367. <p>
  368. This method also generates the code to call the static
  369. ClassGeneratorUtil
  370. getConstructorArgs() method which inspects the scripted constructor to
  371. find the alternate constructor signature (if any) and evalute the
  372. arguments at runtime. The getConstructorArgs() method returns the
  373. actual arguments as well as the index of the constructor to call.
  374. */
  375. void generateConstructorSwitch(
  376. int consIndex, int argsVar, int consArgsVar, CodeVisitor cv )
  377. {
  378. Label defaultLabel = new Label();
  379. Label endLabel = new Label();
  380. int cases = superConstructors.length + constructors.length ;
  381. Label [] labels = new Label[ cases ];
  382. for(int i=0; i<cases; i++)
  383. labels[i]=new Label();
  384. // Generate code to call ClassGeneratorUtil to get our switch index
  385. // and give us args...
  386. // push super class name
  387. cv.visitLdcInsn( superClass.getName() ); // use superClassName var?
  388. // push class static This object
  389. cv.visitFieldInsn(
  390. GETSTATIC, fqClassName, BSHSTATIC+className, "Lorg/gjt/sp/jedit/bsh/This;" );
  391. // push args
  392. cv.visitVarInsn( ALOAD, argsVar );
  393. // push this constructor index number onto stack
  394. cv.visitIntInsn( BIPUSH, consIndex );
  395. // invoke the ClassGeneratorUtil getConstructorsArgs() method
  396. cv.visitMethodInsn(
  397. INVOKESTATIC, "org/gjt/sp/jedit/bsh/ClassGeneratorUtil", "getConstructorArgs",
  398. "(Ljava/lang/String;Lorg/gjt/sp/jedit/bsh/This;[Ljava/lang/Object;I)"
  399. +"Lorg/gjt/sp/jedit/bsh/ClassGeneratorUtil$ConstructorArgs;"
  400. );
  401. // store ConstructorArgs in consArgsVar
  402. cv.visitVarInsn( ASTORE, consArgsVar );
  403. // Get the ConstructorArgs selector field from ConstructorArgs
  404. // push ConstructorArgs
  405. cv.visitVarInsn( ALOAD, consArgsVar );
  406. cv.visitFieldInsn(
  407. GETFIELD, "org/gjt/sp/jedit/bsh/ClassGeneratorUtil$ConstructorArgs", "selector", "I" );
  408. // start switch
  409. cv.visitTableSwitchInsn(
  410. 0/*min*/, cases-1/*max*/, defaultLabel, labels );
  411. // generate switch body
  412. int index = 0;
  413. for( int i=0; i< superConstructors.length; i++, index++)
  414. doSwitchBranch( index, superClassName,
  415. getTypeDescriptors( superConstructors[i].getParameterTypes() ),
  416. endLabel, labels, consArgsVar, cv );
  417. for( int i=0; i< constructors.length; i++, index++)
  418. doSwitchBranch( index, fqClassName,
  419. constructors[i].getParamTypeDescriptors(),
  420. endLabel, labels, consArgsVar, cv );
  421. // generate the default branch of switch
  422. cv.visitLabel( defaultLabel );
  423. // default branch always invokes no args super
  424. cv.visitVarInsn( ALOAD, 0 ); // push 'this'
  425. cv.visitMethodInsn( INVOKESPECIAL, superClassName, "<init>", "()V" );
  426. // done with switch
  427. cv.visitLabel( endLabel );
  428. }
  429. /*
  430. Generate a branch of the constructor switch. This method is called by
  431. generateConstructorSwitch.
  432. The code generated by this method assumes that the argument array is
  433. on the stack.
  434. */
  435. static void doSwitchBranch(
  436. int index, String targetClassName, String [] paramTypes,
  437. Label endLabel, Label [] labels, int consArgsVar, CodeVisitor cv
  438. )
  439. {
  440. cv.visitLabel( labels[index] );
  441. //cv.visitLineNumber( index, labels[index] );
  442. cv.visitVarInsn( ALOAD, 0 ); // push this before args
  443. // Unload the arguments from the ConstructorArgs object
  444. for (int i=0; i<paramTypes.length; i++)
  445. {
  446. String type = paramTypes[i];
  447. String method = null;
  448. if ( type.equals("Z") )
  449. method = "getBoolean";
  450. else if ( type.equals("B") )
  451. method = "getByte";
  452. else if ( type.equals("C") )
  453. method = "getChar";
  454. else if ( type.equals("S") )
  455. method = "getShort";
  456. else if ( type.equals("I") )
  457. method = "getInt";
  458. else if ( type.equals("J") )
  459. method = "getLong";
  460. else if ( type.equals("D") )
  461. method = "getDouble";
  462. else if ( type.equals("F") )
  463. method = "getFloat";
  464. else
  465. method = "getObject";
  466. // invoke the iterator method on the ConstructorArgs
  467. cv.visitVarInsn( ALOAD, consArgsVar ); // push the ConstructorArgs
  468. String className = "org/gjt/sp/jedit/bsh/ClassGeneratorUtil$ConstructorArgs";
  469. String retType;
  470. if ( method.equals("getObject") )
  471. retType = OBJECT;
  472. else
  473. retType = type;
  474. cv.visitMethodInsn(INVOKEVIRTUAL, className, method, "()"+retType);
  475. // if it's an object type we must do a check cast
  476. if ( method.equals("getObject") )
  477. cv.visitTypeInsn( CHECKCAST, descriptorToClassName(type) );
  478. }
  479. // invoke the constructor for this branch
  480. String descriptor = getMethodDescriptor( "V", paramTypes );
  481. cv.visitMethodInsn(
  482. INVOKESPECIAL, targetClassName, "<init>", descriptor );
  483. cv.visitJumpInsn( GOTO, endLabel );
  484. }
  485. static String getMethodDescriptor( String returnType, String [] paramTypes )
  486. {
  487. StringBuilder sb = new StringBuilder("(");
  488. for(int i=0; i<paramTypes.length; i++)
  489. sb.append(paramTypes[i]);
  490. sb.append(")"+returnType);
  491. return sb.toString();
  492. }
  493. /**
  494. Generate a superclass method delegate accessor method.
  495. These methods are specially named methods which allow access to
  496. overridden methods of the superclass (which the Java reflection API
  497. normally does not allow).
  498. */
  499. // Maybe combine this with generateMethod()
  500. static void generateSuperDelegateMethod( String superClassName, String methodName,
  501. String returnType, String[] paramTypes, int modifiers, ClassWriter cw )
  502. {
  503. String [] exceptions = null;
  504. if ( returnType == null ) // map loose return to Object
  505. returnType = OBJECT;
  506. String methodDescriptor = getMethodDescriptor( returnType, paramTypes );
  507. // Add method body
  508. CodeVisitor cv = cw.visitMethod(
  509. modifiers, "_bshSuper"+methodName, methodDescriptor, exceptions );
  510. cv.visitVarInsn(ALOAD, 0);
  511. // Push vars
  512. int localVarIndex = 1;
  513. for (int i = 0; i < paramTypes.length; ++i)
  514. {
  515. if ( isPrimitive( paramTypes[i]) )
  516. cv.visitVarInsn(ILOAD, localVarIndex);
  517. else
  518. cv.visitVarInsn(ALOAD, localVarIndex);
  519. localVarIndex +=
  520. ( (paramTypes[i].equals("D") || paramTypes[i].equals("J"))
  521. ? 2 : 1 );
  522. }
  523. cv.visitMethodInsn( INVOKESPECIAL,
  524. superClassName, methodName, methodDescriptor );
  525. generatePlainReturnCode( returnType, cv );
  526. // Need to calculate this... just fudging here for now.
  527. cv.visitMaxs( 20, 20 );
  528. }
  529. boolean classContainsMethod(
  530. Class clas, String methodName, String [] paramTypes )
  531. {
  532. while( clas != null )
  533. {
  534. Method [] methods = clas.getDeclaredMethods();
  535. for( int i =0; i<methods.length; i++ )
  536. {
  537. if ( methods[i].getName().equals(methodName) )
  538. {
  539. String [] methodParamTypes =
  540. getTypeDescriptors( methods[i].getParameterTypes() );
  541. boolean found = true;
  542. for( int j=0; j<methodParamTypes.length; j++)
  543. {
  544. if ( ! paramTypes[j].equals( methodParamTypes[j] ) ) {
  545. found = false;
  546. break;
  547. }
  548. }
  549. if ( found )
  550. return true;
  551. }
  552. }
  553. clas = clas.getSuperclass();
  554. }
  555. return false;
  556. }
  557. /**
  558. Generate return code for a normal bytecode
  559. */
  560. static void generatePlainReturnCode( String returnType, CodeVisitor cv )
  561. {
  562. if ( returnType.equals("V") )
  563. cv.visitInsn( RETURN );
  564. else
  565. if ( isPrimitive( returnType ) )
  566. {
  567. int opcode = IRETURN;
  568. if ( returnType.equals("D") )
  569. opcode = DRETURN;
  570. else if ( returnType.equals("F") )
  571. opcode = FRETURN;
  572. else if ( returnType.equals("J") ) //long
  573. opcode = LRETURN;
  574. cv.visitInsn(opcode);
  575. }
  576. else {
  577. cv.visitTypeInsn( CHECKCAST, descriptorToClassName(returnType) );
  578. cv.visitInsn( ARETURN );
  579. }
  580. }
  581. /**
  582. Generates the code to reify the arguments of the given method.
  583. For a method "int m (int i, String s)", this code is the bytecode
  584. corresponding to the "new Object[] { new bsh.Primitive(i), s }"
  585. expression.
  586. @author Eric Bruneton
  587. @author Pat Niemeyer
  588. @param cv the code visitor to be used to generate the bytecode.
  589. @param isStatic the enclosing methods is static
  590. */
  591. public static void generateParameterReifierCode (
  592. String [] paramTypes, boolean isStatic, final CodeVisitor cv )
  593. {
  594. cv.visitIntInsn(SIPUSH, paramTypes.length);
  595. cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
  596. int localVarIndex = isStatic ? 0 : 1;
  597. for (int i = 0; i < paramTypes.length; ++i)
  598. {
  599. String param = paramTypes[i];
  600. cv.visitInsn(DUP);
  601. cv.visitIntInsn(SIPUSH, i);
  602. if ( isPrimitive( param ) )
  603. {
  604. int opcode;
  605. if (param.equals("F")) {
  606. opcode = FLOAD;
  607. } else if (param.equals("D")) {
  608. opcode = DLOAD;
  609. } else if (param.equals("J")) {
  610. opcode = LLOAD;
  611. } else {
  612. opcode = ILOAD;
  613. }
  614. String type = "org/gjt/sp/jedit/bsh/Primitive";
  615. cv.visitTypeInsn( NEW, type );
  616. cv.visitInsn(DUP);
  617. cv.visitVarInsn(opcode, localVarIndex);
  618. String desc = param; // ok?
  619. cv.visitMethodInsn(
  620. INVOKESPECIAL, type, "<init>", "(" + desc + ")V");
  621. } else {
  622. // Technically incorrect here - we need to wrap null values
  623. // as bsh.Primitive.NULL. However the This.invokeMethod()
  624. // will do that much for us.
  625. // We need to generate a conditional here to test for null
  626. // and return Primitive.NULL
  627. cv.visitVarInsn( ALOAD, localVarIndex );
  628. }
  629. cv.visitInsn(AASTORE);
  630. localVarIndex +=
  631. ( (param.equals("D") || param.equals("J")) ? 2 : 1 );
  632. }
  633. }
  634. /**
  635. Generates the code to unreify the result of the given method. For a
  636. method "int m (int i, String s)", this code is the bytecode
  637. corresponding to the "((Integer)...).intValue()" expression.
  638. @param cv the code visitor to be used to generate the bytecode.
  639. @author Eric Bruneton
  640. @author Pat Niemeyer
  641. */
  642. public static void generateReturnCode (
  643. String returnType, CodeVisitor cv )
  644. {
  645. if ( returnType.equals("V") )
  646. {
  647. cv.visitInsn(POP);
  648. cv.visitInsn(RETURN);
  649. }
  650. else if ( isPrimitive( returnType ) )
  651. {
  652. int opcode = IRETURN;
  653. String type;
  654. String meth;
  655. if ( returnType.equals("B") ) {
  656. type = "java/lang/Byte";
  657. meth = "byteValue";
  658. } else if (returnType.equals("I") ) {
  659. type = "java/lang/Integer";
  660. meth = "intValue";
  661. } else if (returnType.equals("Z") ) {
  662. type = "java/lang/Boolean";
  663. meth = "booleanValue";
  664. } else if (returnType.equals("D") ) {
  665. opcode = DRETURN;
  666. type = "java/lang/Double";
  667. meth = "doubleValue";
  668. } else if (returnType.equals("F") ) {
  669. opcode = FRETURN;
  670. type = "java/lang/Float";
  671. meth = "floatValue";
  672. } else if (returnType.equals("J") ) {
  673. opcode = LRETURN;
  674. type = "java/lang/Long";
  675. meth = "longValue";
  676. } else if (returnType.equals("C") ) {
  677. type = "java/lang/Character";
  678. meth = "charValue";
  679. } else /*if (returnType.equals("S") )*/ {
  680. type = "java/lang/Short";
  681. meth = "shortValue";
  682. }
  683. String desc = returnType;
  684. cv.visitTypeInsn( CHECKCAST, type ); // type is correct here
  685. cv.visitMethodInsn( INVOKEVIRTUAL, type, meth, "()" + desc );
  686. cv.visitInsn(opcode);
  687. } else
  688. {
  689. cv.visitTypeInsn( CHECKCAST, descriptorToClassName(returnType) );
  690. cv.visitInsn(ARETURN);
  691. }
  692. }
  693. /**
  694. Evaluate the arguments (if any) for the constructor specified by
  695. the constructor index. Return the ConstructorArgs object which
  696. contains the actual arguments to the alternate constructor and also the
  697. index of that constructor for the constructor switch.
  698. @param consArgs the arguments to the constructor. These are necessary in
  699. the evaluation of the alt constructor args. e.g. Foo(a) { super(a); }
  700. @return the ConstructorArgs object containing a constructor selector
  701. and evaluated arguments for the alternate constructor
  702. */
  703. public static ConstructorArgs getConstructorArgs(
  704. String superClassName, This classStaticThis,
  705. Object [] consArgs, int index )
  706. {
  707. DelayedEvalBshMethod [] constructors;
  708. try {
  709. constructors =
  710. (DelayedEvalBshMethod [])classStaticThis.getNameSpace()
  711. .getVariable( BSHCONSTRUCTORS );
  712. } catch ( Exception e ) {
  713. throw new InterpreterError(
  714. "unable to get instance initializer: "+e );
  715. }
  716. if ( index == DEFAULTCONSTRUCTOR ) // auto-gen default constructor
  717. return ConstructorArgs.DEFAULT; // use default super constructor
  718. DelayedEvalBshMethod constructor = constructors[index];
  719. if ( constructor.methodBody.jjtGetNumChildren() == 0 )
  720. return ConstructorArgs.DEFAULT; // use default super constructor
  721. // Determine if the constructor calls this() or super()
  722. String altConstructor = null;
  723. BSHArguments argsNode = null;
  724. SimpleNode firstStatement =
  725. (SimpleNode)constructor.methodBody.jjtGetChild(0);
  726. if ( firstStatement instanceof BSHPrimaryExpression )
  727. firstStatement = (SimpleNode)firstStatement.jjtGetChild(0);
  728. if ( firstStatement instanceof BSHMethodInvocation )
  729. {
  730. BSHMethodInvocation methodNode =
  731. (BSHMethodInvocation)firstStatement;
  732. BSHAmbiguousName methodName = methodNode.getNameNode();
  733. if ( methodName.text.equals("super")
  734. || methodName.text.equals("this")
  735. ) {
  736. altConstructor = methodName.text;
  737. argsNode = methodNode.getArgsNode();
  738. }
  739. }
  740. if ( altConstructor == null )
  741. return ConstructorArgs.DEFAULT; // use default super constructor
  742. // Make a tmp namespace to hold the original constructor args for
  743. // use in eval of the parameters node
  744. NameSpace consArgsNameSpace =
  745. new NameSpace( classStaticThis.getNameSpace(), "consArgs" );
  746. String [] consArgNames = constructor.getParameterNames();
  747. Class [] consArgTypes = constructor.getParameterTypes();
  748. for( int i=0; i<consArgs.length; i++ )
  749. {
  750. try {
  751. consArgsNameSpace.setTypedVariable(
  752. consArgNames[i], consArgTypes[i], consArgs[i],
  753. null/*modifiers*/);
  754. } catch ( UtilEvalError e ) {
  755. throw new InterpreterError("err setting local cons arg:"+e);
  756. }
  757. }
  758. // evaluate the args
  759. CallStack callstack = new CallStack();
  760. callstack.push( consArgsNameSpace);
  761. Object [] args = null;
  762. Interpreter interpreter = classStaticThis.declaringInterpreter;
  763. try {
  764. args = argsNode.getArguments( callstack, interpreter );
  765. } catch ( EvalError e ) {
  766. throw new InterpreterError(
  767. "Error evaluating constructor args: "+e );
  768. }
  769. Class [] argTypes = Types.getTypes( args );
  770. args = Primitive.unwrap( args );
  771. Class superClass =
  772. interpreter.getClassManager().classForName( superClassName );
  773. if ( superClass == null )
  774. throw new InterpreterError(
  775. "can't find superclass: "+superClassName );
  776. Constructor [] superCons = superClass.getDeclaredConstructors();
  777. // find the matching super() constructor for the args
  778. if ( altConstructor.equals("super") )
  779. {
  780. int i = Reflect.findMostSpecificConstructorIndex(
  781. argTypes , superCons );
  782. if ( i == -1 )
  783. throw new InterpreterError("can't find constructor for args!");
  784. return new ConstructorArgs( i, args );
  785. }
  786. // find the matching this() constructor for the args
  787. Class [][] candidates = new Class [ constructors.length ] [];
  788. for(int i=0; i< candidates.length; i++ )
  789. candidates[i] = constructors[i].getParameterTypes();
  790. int i = Reflect.findMostSpecificSignature( argTypes, candidates );
  791. if ( i == -1 )
  792. throw new InterpreterError("can't find constructor for args 2!");
  793. // this() constructors come after super constructors in the table
  794. int selector = i+superCons.length;
  795. int ourSelector = index+superCons.length;
  796. // Are we choosing ourselves recursively through a this() reference?
  797. if ( selector == ourSelector )
  798. throw new InterpreterError( "Recusive constructor call.");
  799. return new ConstructorArgs( selector, args );
  800. }
  801. /**
  802. Initialize an instance of the class.
  803. This method is called from the generated class constructor to evaluate
  804. the instance initializer and scripted constructor in the instance
  805. namespace.
  806. */
  807. public static void initInstance(
  808. Object instance, String className, Object [] args )
  809. {
  810. Class [] sig = Types.getTypes( args );
  811. CallStack callstack = new CallStack();
  812. Interpreter interpreter;
  813. NameSpace instanceNameSpace;
  814. // check to see if the instance has already been initialized
  815. // (the case if using a this() alternate constuctor)
  816. This instanceThis = getClassInstanceThis( instance, className );
  817. // XXX clean up this conditional
  818. if ( instanceThis == null )
  819. {
  820. // Create the instance 'This' namespace, set it on the object
  821. // instance and invoke the instance initializer
  822. // Get the static This reference from the proto-instance
  823. This classStaticThis =
  824. getClassStaticThis( instance.getClass(), className );
  825. interpreter = classStaticThis.declaringInterpreter;
  826. // Get the instance initializer block from the static This
  827. BSHBlock instanceInitBlock;
  828. try {
  829. instanceInitBlock = (BSHBlock)classStaticThis.getNameSpace()
  830. .getVariable( BSHINIT );
  831. } catch ( Exception e ) {
  832. throw new InterpreterError(
  833. "unable to get instance initializer: "+e );
  834. }
  835. // Create the instance namespace
  836. instanceNameSpace =
  837. new NameSpace( classStaticThis.getNameSpace(), className );
  838. instanceNameSpace.isClass = true;
  839. // Set the instance This reference on the instance
  840. instanceThis = instanceNameSpace.getThis( interpreter );
  841. try {
  842. LHS lhs =
  843. Reflect.getLHSObjectField( instance, BSHTHIS+className );
  844. lhs.assign( instanceThis, false/*strict*/ );
  845. } catch ( Exception e ) {
  846. throw new InterpreterError("Error in class gen setup: "+e );
  847. }
  848. // Give the instance space its object import
  849. instanceNameSpace.setClassInstance( instance );
  850. // should use try/finally here to pop ns
  851. callstack.push( instanceNameSpace );
  852. // evaluate the instance portion of the block in it
  853. try { // Evaluate the initializer block
  854. instanceInitBlock.evalBlock(
  855. callstack, interpreter, true/*override*/,
  856. ClassGeneratorImpl.ClassNodeFilter.CLASSINSTANCE );
  857. } catch ( Exception e ) {
  858. throw new InterpreterError("Error in class initialization: "+e);
  859. }
  860. callstack.pop();
  861. } else
  862. {
  863. // The object instance has already been initialzed by another
  864. // constructor. Fall through to invoke the constructor body below.
  865. interpreter = instanceThis.declaringInterpreter;
  866. instanceNameSpace = instanceThis.getNameSpace();
  867. }
  868. // invoke the constructor method from the instanceThis
  869. String constructorName = getBaseName( className );
  870. try {
  871. // Find the constructor (now in the instance namespace)
  872. BshMethod constructor = instanceNameSpace.getMethod(
  873. constructorName, sig, true/*declaredOnly*/ );
  874. // if args, we must have constructor
  875. if ( args.length > 0 && constructor == null )
  876. throw new InterpreterError(
  877. "Can't find constructor: "+ className );
  878. // Evaluate the constructor
  879. if ( constructor != null )
  880. constructor.invoke( args, interpreter, callstack,
  881. null/*callerInfo*/, false/*overrideNameSpace*/ ) ;
  882. } catch ( Exception e ) {
  883. if ( e instanceof TargetError )
  884. e =(Exception)((TargetError)e).getTarget();
  885. if ( e instanceof InvocationTargetException )
  886. e = (Exception)((InvocationTargetException)e)
  887. .getTargetException();
  888. e.printStackTrace( System.err );
  889. throw new InterpreterError("Error in class initialization: "+e );
  890. }
  891. }
  892. /**
  893. Get the static bsh namespace field from the class.
  894. @param className may be the name of clas itself or a superclass of clas.
  895. */
  896. static This getClassStaticThis( Class clas, String className )
  897. {
  898. try {
  899. return (This)Reflect.getStaticFieldValue(
  900. clas, BSHSTATIC + className );
  901. } catch ( Exception e ) {
  902. throw new InterpreterError("Unable to get class static space: "+e);
  903. }
  904. }
  905. /**
  906. Get the instance bsh namespace field from the object instance.
  907. @return the class instance This object or null if the object has not
  908. been initialized.
  909. */
  910. static This getClassInstanceThis( Object instance, String className )
  911. {
  912. try {
  913. Object o = Reflect.getObjectFieldValue( instance, BSHTHIS+className );
  914. return (This)Primitive.unwrap(o); // unwrap Primitive.Null to null
  915. } catch ( Exception e ) {
  916. throw new InterpreterError(
  917. "Generated class: Error getting This"+e );
  918. }
  919. }
  920. /**
  921. Does the type descriptor string describe a primitive type?
  922. */
  923. private static boolean isPrimitive( String typeDescriptor )
  924. {
  925. return typeDescriptor.length() == 1; // right?
  926. }
  927. static String[] getTypeDescriptors( Class [] cparams )
  928. {
  929. String [] sa = new String [cparams.length];
  930. for(int i=0; i<sa.length; i++)
  931. sa[i] = BSHType.getTypeDescriptor( cparams[i] );
  932. return sa;
  933. }
  934. /**
  935. If a non-array object type, remove the prefix "L" and suffix ";".
  936. */
  937. // Can this be factored out...?
  938. // Should be be adding the L...; here instead?
  939. private static String descriptorToClassName( String s )
  940. {
  941. if ( s.startsWith("[") || !s.startsWith("L") )
  942. return s;
  943. return s.substring( 1, s.length()-1 );
  944. }
  945. private static String getBaseName( String className )
  946. {
  947. int i = className.indexOf("$");
  948. if ( i == -1 )
  949. return className;
  950. return className.substring(i+1);
  951. }
  952. /**
  953. A ConstructorArgs object holds evaluated arguments for a constructor
  954. call as well as the index of a possible alternate selector to invoke.
  955. This object is used by the constructor switch.
  956. @see #generateConstructor( int , String [] , int , ClassWriter )
  957. */
  958. public static class ConstructorArgs
  959. {
  960. /** A ConstructorArgs which calls the default constructor */
  961. public static ConstructorArgs DEFAULT = new ConstructorArgs();
  962. public int selector = DEFAULTCONSTRUCTOR;
  963. Object [] args;
  964. int arg = 0;
  965. /**
  966. The index of the constructor to call.
  967. */
  968. ConstructorArgs() { }
  969. ConstructorArgs( int selector, Object [] args ) {
  970. this.selector = selector;
  971. this.args = args;
  972. }
  973. Object next() { return args[arg++]; }
  974. public boolean getBoolean() { return ((Boolean)next()).booleanValue(); }
  975. public byte getByte() { return ((Byte)next()).byteValue(); }
  976. public char getChar() { return ((Character)next()).charValue(); }
  977. public short getShort() { return ((Short)next()).shortValue(); }
  978. public int getInt() { return ((Integer)next()).intValue(); }
  979. public long getLong() { return ((Long)next()).longValue(); }
  980. public double getDouble() { return ((Double)next()).doubleValue(); }
  981. public float getFloat() { return ((Float)next()).floatValue(); }
  982. public Object getObject() { return next(); }
  983. }
  984. }