PageRenderTime 238ms CodeModel.GetById 53ms app.highlight 158ms RepoModel.GetById 1ms app.codeStats 1ms

/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
   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 org.gjt.sp.jedit.bsh;
  35
  36import org.gjt.sp.jedit.bsh.org.objectweb.asm.*;
  37import org.gjt.sp.jedit.bsh.org.objectweb.asm.Type;
  38
  39import java.lang.reflect.*;
  40import java.util.ArrayList;
  41import java.util.List;
  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*/
  80public 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 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 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 BSHSUPER="_bshSuper";
  93
  94	/** The bsh static namespace variable name of the instance initializer */
  95	static final String BSHINIT="_bshInstanceInitializer";
  96
  97	/** The bsh static namespace variable that holds the constructor methods */
  98	static final String 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 OBJECT= "Ljava/lang/Object;";
 105
 106	String className;
 107	/** fully qualified class name (with package) e.g. foo/bar/Blah */
 108	String fqClassName;
 109	Class superClass; 
 110	String superClassName;
 111	Class [] 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 className, String packageName, 
 125		Class superClass, Class [] 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.class;
 138		this.superClass = superClass;
 139		this.superClassName = Type.getInternalName( superClass );
 140		if ( interfaces == null )
 141			interfaces = new Class[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 consl = new ArrayList();
 149		List methodsl = new ArrayList();
 150		String 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 [] interfaceNames = new String [interfaces.length];
 183		for(int i=0; i<interfaces.length; i++)
 184			interfaceNames[i]=Type.getInternalName( interfaces[i] );
 185
 186		String 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, "Lorg/gjt/sp/jedit/bsh/This;", ACC_PUBLIC, cw);
 196
 197			// Generate the static bsh static reference holder field
 198			generateField( 
 199				BSHSTATIC+className, "Lorg/gjt/sp/jedit/bsh/This;", ACC_PUBLIC+ACC_STATIC, cw);
 200		}
 201
 202		// Generate the fields
 203		for( int i=0; i<vars.length; i++)
 204		{
 205			String 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 [0], ACC_PUBLIC, cw );
 239
 240		// Generate the delegate methods
 241		for( int i=0; i<methods.length; i++)
 242		{
 243			String 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 fieldName, String 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 className, String fqClassName, String methodName, 
 311		String returnType, String[] paramTypes, int modifiers, ClassWriter cw ) 
 312	{
 313		String [] 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 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, "Lorg/gjt/sp/jedit/bsh/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, "Lorg/gjt/sp/jedit/bsh/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, "org/gjt/sp/jedit/bsh/This", "invokeMethod",
 360			Type.getMethodDescriptor( 
 361				Type.getType(Object.class),
 362				new Type [] { 
 363					Type.getType(String.class), 
 364					Type.getType(Object [].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, "org/gjt/sp/jedit/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 [] 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 [] exceptions = null;
 397		String 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, "org/gjt/sp/jedit/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, "Lorg/gjt/sp/jedit/bsh/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, "org/gjt/sp/jedit/bsh/ClassGeneratorUtil", "getConstructorArgs",
 475			"(Ljava/lang/String;Lorg/gjt/sp/jedit/bsh/This;[Ljava/lang/Object;I)"
 476			+"Lorg/gjt/sp/jedit/bsh/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, "org/gjt/sp/jedit/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 targetClassName, String [] 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 type = paramTypes[i];
 533			String 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 className = "org/gjt/sp/jedit/bsh/ClassGeneratorUtil$ConstructorArgs";
 556			String 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 descriptor = getMethodDescriptor( "V", paramTypes );
 569		cv.visitMethodInsn( 
 570			INVOKESPECIAL, targetClassName, "<init>", descriptor );
 571		cv.visitJumpInsn( GOTO, endLabel );
 572	}
 573	
 574	static String getMethodDescriptor( String returnType, String [] paramTypes )
 575	{
 576		StringBuilder sb = new StringBuilder("(");
 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 superClassName, String methodName,
 591											 String returnType, String[] paramTypes, int modifiers, ClassWriter cw )
 592	{
 593		String [] exceptions = null;
 594
 595		if ( returnType == null ) // map loose return to Object
 596			returnType = OBJECT;
 597
 598		String 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 clas, String methodName, String [] 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 [] 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 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 [] 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 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 type = "org/gjt/sp/jedit/bsh/Primitive";
 720				cv.visitTypeInsn( NEW, type );
 721				cv.visitInsn(DUP);
 722				cv.visitVarInsn(opcode, localVarIndex);
 723				String 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 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 type;
 761			String 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 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 superClassName, This classStaticThis, 
 815		Object [] consArgs, int index )
 816	{
 817		DelayedEvalBshMethod [] constructors;
 818		try {
 819			constructors = 
 820				(DelayedEvalBshMethod [])classStaticThis.getNameSpace()
 821				.getVariable( BSHCONSTRUCTORS );
 822		} catch ( Exception 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 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 [] consArgNames = constructor.getParameterNames();
 863		Class [] 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 [] 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 [] argTypes  = Types.getTypes( args );
 890		args = Primitive.unwrap( args );
 891		Class 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 [][] candidates = new Class [ 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 instance, String className, Object [] args )
 935	{
 936		Class [] 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 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 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 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 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 e ) {
1024			if ( e instanceof TargetError )
1025				e =(Exception)((TargetError)e).getTarget();
1026			if ( e instanceof InvocationTargetException )
1027				e = (Exception)((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 clas, String className )
1039	{
1040		try {
1041			return (This)Reflect.getStaticFieldValue(
1042				clas, BSHSTATIC + className );
1043		} catch ( Exception 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 instance, String className )
1054	{
1055		try {
1056			Object o = Reflect.getObjectFieldValue( instance, BSHTHIS+className );
1057			return (This)Primitive.unwrap(o); // unwrap Primitive.Null to null
1058		} catch ( Exception 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 typeDescriptor )
1068	{
1069		return typeDescriptor.length() == 1; // right?
1070	}
1071
1072	static String[] getTypeDescriptors( Class [] cparams )
1073	{
1074		String [] sa = new String [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 descriptorToClassName( String 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 getBaseName( String 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 [] args;
1114		int arg = 0;
1115		/**
1116			The index of the constructor to call.
1117		*/
1118
1119		ConstructorArgs() {  }
1120
1121		ConstructorArgs( int selector, Object [] args ) { 
1122			this.selector = selector;
1123			this.args = args; 
1124		}
1125
1126		Object next() { return args[arg++]; }
1127
1128		public boolean getBoolean() { return ((Boolean)next()).booleanValue(); }
1129		public byte getByte() { return ((Byte)next()).byteValue(); }
1130		public char getChar() { return ((Character)next()).charValue(); }
1131		public short getShort() { return ((Short)next()).shortValue(); }
1132		public int getInt() { return ((Integer)next()).intValue(); }
1133		public long getLong() { return ((Long)next()).longValue(); }
1134		public double getDouble() { return ((Double)next()).doubleValue(); }
1135		public float getFloat() { return ((Float)next()).floatValue(); }
1136		public Object getObject() { return next(); }
1137	}
1138}