/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}