PageRenderTime 221ms CodeModel.GetById 195ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
Java | 327 lines | 225 code | 51 blank | 51 comment | 26 complexity | 098ca364a7dee4c0bbde5bcb27630e55 MD5 | raw file
  1package bsh;
  2
  3import java.io.*;
  4import java.util.*;
  5import java.lang.reflect.InvocationTargetException;
  6import java.lang.reflect.Method;
  7
  8/**
  9	
 10	@author Pat Niemeyer (pat@pat.net)
 11*/
 12public class ClassGeneratorImpl extends ClassGenerator
 13{
 14	public Class generateClass( 
 15		String name, Modifiers modifiers, 
 16		Class [] interfaces, Class superClass, BSHBlock block, 
 17		boolean isInterface, CallStack callstack, Interpreter interpreter 
 18	)
 19		throws EvalError
 20	{
 21		// Delegate to the static method
 22		return generateClassImpl( name, modifiers, interfaces, superClass,
 23			block, isInterface, callstack, interpreter );
 24	}
 25
 26	public Object invokeSuperclassMethod(
 27		BshClassManager bcm, Object instance, String methodName, Object [] args
 28	)
 29        throws UtilEvalError, ReflectError, InvocationTargetException
 30	{
 31		// Delegate to the static method
 32		return invokeSuperclassMethodImpl( bcm, instance, methodName, args );
 33	}
 34
 35	/**
 36		Change the parent of the class instance namespace.
 37		This is currently used for inner class support.
 38		Note: This method will likely be removed in the future.
 39	*/
 40	// This could be static
 41	public void setInstanceNameSpaceParent( 
 42		Object instance, String className, NameSpace parent )
 43	{
 44		This ithis = 
 45			ClassGeneratorUtil.getClassInstanceThis( instance, className );
 46		ithis.getNameSpace().setParent( parent );
 47	}
 48
 49	/**
 50		Parse the BSHBlock for for the class definition and generate the class
 51		using ClassGenerator.
 52	*/
 53	public static Class generateClassImpl( 
 54		String name, Modifiers modifiers, 
 55		Class [] interfaces, Class superClass, BSHBlock block, 
 56		boolean isInterface, CallStack callstack, Interpreter interpreter 
 57	)
 58		throws EvalError
 59	{
 60		// Scripting classes currently requires accessibility
 61		// This can be eliminated with a bit more work.
 62		Capabilities.setAccessibility( true );	
 63		if ( !Capabilities.haveAccessibility() )
 64			throw new InterpreterError(
 65				"Defining classes currently requires reflect Accessibility.");
 66
 67		NameSpace enclosingNameSpace = callstack.top();
 68		String packageName = enclosingNameSpace.getPackage();
 69		String className =  enclosingNameSpace.isClass ?  
 70			( enclosingNameSpace.getName()+"$"+name ) : name;
 71		String fqClassName = 
 72			packageName == null ? className : packageName + "." + className;
 73
 74		BshClassManager bcm = interpreter.getClassManager();
 75		// Race condition here...
 76		bcm.definingClass( fqClassName );
 77
 78		// Create the class static namespace
 79		NameSpace classStaticNameSpace = 
 80			new NameSpace( enclosingNameSpace, className);
 81		classStaticNameSpace.isClass = true;
 82
 83		callstack.push( classStaticNameSpace );
 84
 85		// Evaluate any inner class class definitions in the block 
 86		// effectively recursively call this method for contained classes first
 87		block.evalBlock( 
 88			callstack, interpreter, true/*override*/, 
 89			ClassNodeFilter.CLASSCLASSES );
 90
 91		// Generate the type for our class
 92		Variable [] variables = 
 93			getDeclaredVariables( block, callstack, interpreter, packageName );
 94		DelayedEvalBshMethod [] methods =
 95			getDeclaredMethods( block, callstack, interpreter, packageName );
 96
 97		ClassGeneratorUtil classGenerator = new ClassGeneratorUtil( 
 98			modifiers, className, packageName, superClass, interfaces, 
 99			variables, methods, classStaticNameSpace, isInterface );
100		byte [] code = classGenerator.generateClass();
101
102		// if debug, write out the class file to debugClasses directory
103		String dir = System.getProperty("debugClasses");
104		if ( dir != null )
105		try {
106			FileOutputStream out= 
107				new FileOutputStream( dir+"/"+className+".class" );
108			out.write(code);
109			out.close();
110		} catch ( IOException e ) { }
111
112		// Define the new class in the classloader
113		Class genClass = bcm.defineClass( fqClassName, code );
114
115//bcm.doneDefiningClass( fqClassName );
116
117		// import the unq name into parent
118		enclosingNameSpace.importClass( fqClassName.replace('$','.') );
119
120		// Also cache the class so that no import resolution must occur
121		// this avoids having to load our enclosing class which isn't
122		// finished being generated yet... oy.
123
124// caching is not correct and doesn't seem to help...
125//	enclosingNameSpace.cacheClass( name, genClass );
126
127// Also cache it in the static namespace...
128//classStaticNameSpace.cacheClass( name, genClass );
129
130//classStaticNameSpace.importClass( fqClassName.replace('$','.') );
131
132		try {
133			classStaticNameSpace.setLocalVariable( 
134				ClassGeneratorUtil.BSHINIT, block, false/*strictJava*/ );
135		} catch ( UtilEvalError e ) {
136			throw new InterpreterError("unable to init static: "+e );
137		}
138
139		// Give the static space its class static import
140		// important to do this after all classes are defined
141		classStaticNameSpace.setClassStatic( genClass );
142
143		// evaluate the static portion of the block in the static space
144		block.evalBlock( 
145			callstack, interpreter, true/*override*/, 
146			ClassNodeFilter.CLASSSTATIC );
147
148		callstack.pop();
149
150		if ( !genClass.isInterface() )
151		{
152		// Set the static bsh This callback 
153		String bshStaticFieldName = ClassGeneratorUtil.BSHSTATIC+className;
154		try {
155			LHS lhs = Reflect.getLHSStaticField( genClass, bshStaticFieldName );
156			lhs.assign( 
157				classStaticNameSpace.getThis( interpreter ), false/*strict*/ );
158		} catch ( Exception e ) {
159			throw new InterpreterError("Error in class gen setup: "+e );
160		}
161		}
162
163		bcm.doneDefiningClass( fqClassName );
164		return genClass;
165	}
166
167	static Variable [] getDeclaredVariables( 
168		BSHBlock body, CallStack callstack, Interpreter interpreter, 
169		String defaultPackage 
170	) 
171		throws EvalError
172	{
173		List vars = new ArrayList();
174		for( int child=0; child<body.jjtGetNumChildren(); child++ )
175		{
176			SimpleNode node = (SimpleNode)body.jjtGetChild(child);
177			if ( node instanceof BSHTypedVariableDeclaration )
178			{
179				BSHTypedVariableDeclaration tvd = 
180					(BSHTypedVariableDeclaration)node;
181				Modifiers modifiers = tvd.modifiers;
182
183				String type = tvd.getTypeDescriptor( 
184					callstack, interpreter, defaultPackage );
185
186				BSHVariableDeclarator [] vardec = tvd.getDeclarators();
187				for( int i = 0; i< vardec.length; i++)
188				{
189					String name = vardec[i].name;
190					try {
191						Variable var = new Variable( 
192							name, type, null/*value*/, modifiers );
193						vars.add( var );
194					} catch ( UtilEvalError e ) {
195						// value error shouldn't happen
196					}
197				}
198			}
199		}
200
201		return (Variable [])vars.toArray( new Variable[0] );
202	}
203
204	static DelayedEvalBshMethod [] getDeclaredMethods( 
205		BSHBlock body, CallStack callstack, Interpreter interpreter,
206		String defaultPackage 
207	)
208		throws EvalError
209	{
210		List methods = new ArrayList();
211		for( int child=0; child<body.jjtGetNumChildren(); child++ )
212		{
213			SimpleNode node = (SimpleNode)body.jjtGetChild(child);
214			if ( node instanceof BSHMethodDeclaration )
215			{
216				BSHMethodDeclaration md = (BSHMethodDeclaration)node;
217				md.insureNodesParsed();
218				Modifiers modifiers = md.modifiers;
219				String name = md.name;
220				String returnType = md.getReturnTypeDescriptor( 
221					callstack, interpreter, defaultPackage );
222				BSHReturnType returnTypeNode = md.getReturnTypeNode();
223				BSHFormalParameters paramTypesNode = md.paramsNode;
224				String [] paramTypes = paramTypesNode.getTypeDescriptors( 
225					callstack, interpreter, defaultPackage );
226
227				DelayedEvalBshMethod bm = new DelayedEvalBshMethod( 
228					name, 
229					returnType, returnTypeNode,
230					md.paramsNode.getParamNames(), 
231					paramTypes, paramTypesNode,
232					md.blockNode, null/*declaringNameSpace*/,
233					modifiers, callstack, interpreter 
234				);
235
236				methods.add( bm );
237			}
238		}
239
240		return (DelayedEvalBshMethod [])methods.toArray( 
241			new DelayedEvalBshMethod[0] );
242	}
243
244	/**
245		A node filter that filters nodes for either a class body static 
246		initializer or instance initializer.  In the static case only static 
247		members are passed, etc.  
248	*/
249	static class ClassNodeFilter implements BSHBlock.NodeFilter
250	{
251		public static final int STATIC=0, INSTANCE=1, CLASSES=2;
252
253		public static ClassNodeFilter CLASSSTATIC = 
254			new ClassNodeFilter( STATIC );
255		public static ClassNodeFilter CLASSINSTANCE = 
256			new ClassNodeFilter( INSTANCE );
257		public static ClassNodeFilter CLASSCLASSES = 
258			new ClassNodeFilter( CLASSES );
259
260		int context;
261
262		private ClassNodeFilter( int context ) { this.context = context; }
263
264		public boolean isVisible( SimpleNode node ) 
265		{
266			if ( context == CLASSES )
267				return node instanceof BSHClassDeclaration;
268
269			// Only show class decs in CLASSES
270			if ( node instanceof BSHClassDeclaration )
271				return false;
272
273			if ( context == STATIC )
274				return isStatic( node );
275
276			if ( context == INSTANCE )
277				return !isStatic( node );
278
279			// ALL
280			return true;
281		}
282
283		boolean isStatic( SimpleNode node ) 
284		{
285			if ( node instanceof BSHTypedVariableDeclaration )
286				return ((BSHTypedVariableDeclaration)node).modifiers != null
287					&& ((BSHTypedVariableDeclaration)node).modifiers
288						.hasModifier("static");
289
290			if ( node instanceof BSHMethodDeclaration )
291				return ((BSHMethodDeclaration)node).modifiers != null
292					&& ((BSHMethodDeclaration)node).modifiers
293						.hasModifier("static");
294
295			// need to add static block here
296			if ( node instanceof BSHBlock)
297				return false;
298
299			return false;
300		}
301	}
302
303	public static Object invokeSuperclassMethodImpl(
304		BshClassManager bcm, Object instance, String methodName, Object [] args
305	)
306        throws UtilEvalError, ReflectError, InvocationTargetException
307	{
308		String superName = ClassGeneratorUtil.BSHSUPER+methodName;
309		
310		// look for the specially named super delegate method
311		Class clas = instance.getClass();
312		Method superMethod = Reflect.resolveJavaMethod(
313			bcm, clas, superName, Types.getTypes(args), false/*onlyStatic*/ );
314		if ( superMethod != null )
315			return Reflect.invokeOnMethod( 
316				superMethod, instance, args );
317
318		// No super method, try to invoke regular method
319		// could be a superfluous "super." which is legal.
320		Class superClass = clas.getSuperclass();
321		superMethod = Reflect.resolveExpectedJavaMethod(
322			bcm, superClass, instance, methodName, args, 
323			false/*onlyStatic*/ );
324		return Reflect.invokeOnMethod( superMethod, instance, args );
325	}
326
327}