PageRenderTime 20ms CodeModel.GetById 6ms app.highlight 11ms RepoModel.GetById 0ms app.codeStats 0ms

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

#
Java | 316 lines | 228 code | 46 blank | 42 comment | 25 complexity | f83ab2c018bd5dc439b841bf9de2b09d MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1package org.gjt.sp.jedit.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		try {
 63			Capabilities.setAccessibility( true );
 64		} catch ( Capabilities.Unavailable e )
 65		{
 66			throw new EvalError( 
 67				"Defining classes currently requires reflective Accessibility.",
 68				block, callstack );
 69		}
 70
 71		NameSpace enclosingNameSpace = callstack.top();
 72		String packageName = enclosingNameSpace.getPackage();
 73		String className =  enclosingNameSpace.isClass ?  
 74			( enclosingNameSpace.getName()+"$"+name ) : name;
 75		String fqClassName = 
 76			packageName == null ? className : packageName + "." + className;
 77
 78		BshClassManager bcm = interpreter.getClassManager();
 79		// Race condition here...
 80		bcm.definingClass( fqClassName );
 81
 82		// Create the class static namespace
 83		NameSpace classStaticNameSpace = 
 84			new NameSpace( enclosingNameSpace, className);
 85		classStaticNameSpace.isClass = true;
 86
 87		callstack.push( classStaticNameSpace );
 88
 89		// Evaluate any inner class class definitions in the block 
 90		// effectively recursively call this method for contained classes first
 91		block.evalBlock( 
 92			callstack, interpreter, true/*override*/, 
 93			ClassNodeFilter.CLASSCLASSES );
 94
 95		// Generate the type for our class
 96		Variable [] variables = 
 97			getDeclaredVariables( block, callstack, interpreter, packageName );
 98		DelayedEvalBshMethod [] methods =
 99			getDeclaredMethods( block, callstack, interpreter, packageName );
100
101		ClassGeneratorUtil classGenerator = new ClassGeneratorUtil( 
102			modifiers, className, packageName, superClass, interfaces, 
103			variables, methods, classStaticNameSpace, isInterface );
104		byte [] code = classGenerator.generateClass();
105
106		// if debug, write out the class file to debugClasses directory
107		String dir = System.getProperty("debugClasses");
108		if ( dir != null )
109		try {
110			FileOutputStream out= 
111				new FileOutputStream( dir+"/"+className+".class" );
112			out.write(code);
113			out.close();
114		} catch ( IOException e ) { }
115
116		// Define the new class in the classloader
117		Class genClass = bcm.defineClass( fqClassName, code );
118
119		// import the unq name into parent
120		enclosingNameSpace.importClass( fqClassName.replace('$','.') );
121
122		try {
123			classStaticNameSpace.setLocalVariable( 
124				ClassGeneratorUtil.BSHINIT, block, false/*strictJava*/ );
125		} catch ( UtilEvalError e ) {
126			throw new InterpreterError("unable to init static: "+e );
127		}
128
129		// Give the static space its class static import
130		// important to do this after all classes are defined
131		classStaticNameSpace.setClassStatic( genClass );
132
133		// evaluate the static portion of the block in the static space
134		block.evalBlock( 
135			callstack, interpreter, true/*override*/, 
136			ClassNodeFilter.CLASSSTATIC );
137
138		callstack.pop();
139
140		if ( !genClass.isInterface() )
141		{
142		// Set the static bsh This callback 
143		String bshStaticFieldName = ClassGeneratorUtil.BSHSTATIC+className;
144		try {
145			LHS lhs = Reflect.getLHSStaticField( genClass, bshStaticFieldName );
146			lhs.assign( 
147				classStaticNameSpace.getThis( interpreter ), false/*strict*/ );
148		} catch ( Exception e ) {
149			throw new InterpreterError("Error in class gen setup: "+e );
150		}
151		}
152
153		bcm.doneDefiningClass( fqClassName );
154		return genClass;
155	}
156
157	static Variable [] getDeclaredVariables( 
158		BSHBlock body, CallStack callstack, Interpreter interpreter, 
159		String defaultPackage 
160	) 
161	{
162		List vars = new ArrayList();
163		for( int child=0; child<body.jjtGetNumChildren(); child++ )
164		{
165			SimpleNode node = (SimpleNode)body.jjtGetChild(child);
166			if ( node instanceof BSHTypedVariableDeclaration )
167			{
168				BSHTypedVariableDeclaration tvd = 
169					(BSHTypedVariableDeclaration)node;
170				Modifiers modifiers = tvd.modifiers;
171
172				String type = tvd.getTypeDescriptor( 
173					callstack, interpreter, defaultPackage );
174
175				BSHVariableDeclarator [] vardec = tvd.getDeclarators();
176				for( int i = 0; i< vardec.length; i++)
177				{
178					String name = vardec[i].name;
179					try {
180						Variable var = new Variable( 
181							name, type, null/*value*/, modifiers );
182						vars.add( var );
183					} catch ( UtilEvalError e ) {
184						// value error shouldn't happen
185					}
186				}
187			}
188		}
189
190		return (Variable [])vars.toArray( new Variable[0] );
191	}
192
193	static DelayedEvalBshMethod [] getDeclaredMethods( 
194		BSHBlock body, CallStack callstack, Interpreter interpreter,
195		String defaultPackage 
196	)
197		throws EvalError
198	{
199		List methods = new ArrayList();
200		for( int child=0; child<body.jjtGetNumChildren(); child++ )
201		{
202			SimpleNode node = (SimpleNode)body.jjtGetChild(child);
203			if ( node instanceof BSHMethodDeclaration )
204			{
205				BSHMethodDeclaration md = (BSHMethodDeclaration)node;
206				md.insureNodesParsed();
207				Modifiers modifiers = md.modifiers;
208				String name = md.name;
209				String returnType = md.getReturnTypeDescriptor( 
210					callstack, interpreter, defaultPackage );
211				BSHReturnType returnTypeNode = md.getReturnTypeNode();
212				BSHFormalParameters paramTypesNode = md.paramsNode;
213				String [] paramTypes = paramTypesNode.getTypeDescriptors( 
214					callstack, interpreter, defaultPackage );
215
216				DelayedEvalBshMethod bm = new DelayedEvalBshMethod( 
217					name, 
218					returnType, returnTypeNode,
219					md.paramsNode.getParamNames(), 
220					paramTypes, paramTypesNode,
221					md.blockNode, null/*declaringNameSpace*/,
222					modifiers, callstack, interpreter 
223				);
224
225				methods.add( bm );
226			}
227		}
228
229		return (DelayedEvalBshMethod [])methods.toArray( 
230			new DelayedEvalBshMethod[0] );
231	}
232
233	/**
234		A node filter that filters nodes for either a class body static 
235		initializer or instance initializer.  In the static case only static 
236		members are passed, etc.  
237	*/
238	static class ClassNodeFilter implements BSHBlock.NodeFilter
239	{
240		public static final int STATIC=0, INSTANCE=1, CLASSES=2;
241
242		public static ClassNodeFilter CLASSSTATIC = 
243			new ClassNodeFilter( STATIC );
244		public static ClassNodeFilter CLASSINSTANCE = 
245			new ClassNodeFilter( INSTANCE );
246		public static ClassNodeFilter CLASSCLASSES = 
247			new ClassNodeFilter( CLASSES );
248
249		int context;
250
251		private ClassNodeFilter( int context ) { this.context = context; }
252
253		public boolean isVisible( SimpleNode node ) 
254		{
255			if ( context == CLASSES )
256				return node instanceof BSHClassDeclaration;
257
258			// Only show class decs in CLASSES
259			if ( node instanceof BSHClassDeclaration )
260				return false;
261
262			if ( context == STATIC )
263				return isStatic( node );
264
265			if ( context == INSTANCE )
266				return !isStatic( node );
267
268			// ALL
269			return true;
270		}
271
272		boolean isStatic( SimpleNode node ) 
273		{
274			if ( node instanceof BSHTypedVariableDeclaration )
275				return ((BSHTypedVariableDeclaration)node).modifiers != null
276					&& ((BSHTypedVariableDeclaration)node).modifiers
277						.hasModifier("static");
278
279			if ( node instanceof BSHMethodDeclaration )
280				return ((BSHMethodDeclaration)node).modifiers != null
281					&& ((BSHMethodDeclaration)node).modifiers
282						.hasModifier("static");
283
284			// need to add static block here
285			if ( node instanceof BSHBlock)
286				return false;
287
288			return false;
289		}
290	}
291
292	public static Object invokeSuperclassMethodImpl(
293		BshClassManager bcm, Object instance, String methodName, Object [] args
294	)
295        throws UtilEvalError, ReflectError, InvocationTargetException
296	{
297		String superName = ClassGeneratorUtil.BSHSUPER+methodName;
298		
299		// look for the specially named super delegate method
300		Class clas = instance.getClass();
301		Method superMethod = Reflect.resolveJavaMethod(
302			bcm, clas, superName, Types.getTypes(args), false/*onlyStatic*/ );
303		if ( superMethod != null )
304			return Reflect.invokeMethod(
305				superMethod, instance, args );
306
307		// No super method, try to invoke regular method
308		// could be a superfluous "super." which is legal.
309		Class superClass = clas.getSuperclass();
310		superMethod = Reflect.resolveExpectedJavaMethod(
311			bcm, superClass, instance, methodName, args, 
312			false/*onlyStatic*/ );
313		return Reflect.invokeMethod( superMethod, instance, args );
314	}
315
316}