PageRenderTime 189ms CodeModel.GetById 89ms app.highlight 87ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-2-pre14/bsh/ClassGeneratorUtil.java

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