001: package bsh;
002:
003: import java.io.*;
004: import java.util.*;
005: import java.lang.reflect.InvocationTargetException;
006: import java.lang.reflect.Method;
007:
008: /**
009: This class is an implementation of the ClassGenerator interface which
010: contains generally bsh related code. The actual bytecode generation is
011: done by ClassGeneratorUtil.
012:
013: @author Pat Niemeyer (pat@pat.net)
014: */
015: public class ClassGeneratorImpl extends ClassGenerator {
016: public Class generateClass(String name, Modifiers modifiers,
017: Class[] interfaces, Class super Class, BSHBlock block,
018: boolean isInterface, CallStack callstack,
019: Interpreter interpreter) throws EvalError {
020: // Delegate to the static method
021: return generateClassImpl(name, modifiers, interfaces,
022: super Class, block, isInterface, callstack, interpreter);
023: }
024:
025: public Object invokeSuperclassMethod(BshClassManager bcm,
026: Object instance, String methodName, Object[] args)
027: throws UtilEvalError, ReflectError,
028: InvocationTargetException {
029: // Delegate to the static method
030: return invokeSuperclassMethodImpl(bcm, instance, methodName,
031: args);
032: }
033:
034: /**
035: Change the parent of the class instance namespace.
036: This is currently used for inner class support.
037: Note: This method will likely be removed in the future.
038: */
039: // This could be static
040: public void setInstanceNameSpaceParent(Object instance,
041: String className, NameSpace parent) {
042: This ithis = ClassGeneratorUtil.getClassInstanceThis(instance,
043: className);
044: ithis .getNameSpace().setParent(parent);
045: }
046:
047: /**
048: If necessary, parse the BSHBlock for for the class definition and
049: generate the class using ClassGeneratorUtil.
050: This method also initializes the static block namespace and sets it
051: in the class.
052: */
053: public static Class generateClassImpl(String name,
054: Modifiers modifiers, Class[] interfaces, Class super Class,
055: BSHBlock block, boolean isInterface, CallStack callstack,
056: Interpreter interpreter) throws EvalError {
057: // Scripting classes currently requires accessibility
058: // This can be eliminated with a bit more work.
059: try {
060: Capabilities.setAccessibility(true);
061: } catch (Capabilities.Unavailable e) {
062: throw new EvalError(
063: "Defining classes currently requires reflective Accessibility.",
064: block, callstack);
065: }
066:
067: NameSpace enclosingNameSpace = callstack.top();
068: String packageName = enclosingNameSpace.getPackage();
069: String className = enclosingNameSpace.isClass ? (enclosingNameSpace
070: .getName()
071: + "$" + name)
072: : name;
073: String fqClassName = packageName == null ? className
074: : packageName + "." + className;
075: String bshStaticFieldName = ClassGeneratorUtil.BSHSTATIC
076: + className;
077:
078: BshClassManager bcm = interpreter.getClassManager();
079: // Race condition here...
080: bcm.definingClass(fqClassName);
081:
082: // Create the class static namespace
083: NameSpace classStaticNameSpace = new NameSpace(
084: enclosingNameSpace, className);
085: classStaticNameSpace.isClass = true;
086:
087: callstack.push(classStaticNameSpace);
088:
089: // Evaluate any inner class class definitions in the block
090: // effectively recursively call this method for contained classes first
091: block.evalBlock(callstack, interpreter, true/*override*/,
092: ClassNodeFilter.CLASSCLASSES);
093:
094: // Generate the type for our class
095: Variable[] variables = getDeclaredVariables(block, callstack,
096: interpreter, packageName);
097: DelayedEvalBshMethod[] methods = getDeclaredMethods(block,
098: callstack, interpreter, packageName);
099:
100: // Create the class generator, which encapsulates all knowledge of the
101: // structure of the class
102: ClassGeneratorUtil classGenerator = new ClassGeneratorUtil(
103: modifiers, className, packageName, super Class,
104: interfaces, variables, methods, isInterface);
105:
106: // Check for existing class (saved class file)
107: Class clas = bcm.getAssociatedClass(fqClassName);
108:
109: // If the class isn't there then generate it.
110: // Else just let it be initialized below.
111: if (clas == null) {
112: // generate bytecode, optionally with static init hooks to
113: // bootstrap the interpreter
114: byte[] code = classGenerator.generateClass(Interpreter
115: .getSaveClasses()/*init code*/);
116:
117: if (Interpreter.getSaveClasses())
118: saveClasses(className, code);
119: else
120: clas = bcm.defineClass(fqClassName, code);
121: }
122:
123: // If we're just saving clases then don't actually execute the static
124: // code for the class here.
125: if (!Interpreter.getSaveClasses()) {
126: // Let the class generator install hooks relating to the structure of
127: // the class into the class static namespace. e.g. the constructor
128: // array. This is necessary whether we are generating code or just
129: // reinitializing a previously generated class.
130: classGenerator.initStaticNameSpace(classStaticNameSpace,
131: block/*instance initializer*/);
132:
133: // import the unqualified class name into parent namespace
134: enclosingNameSpace.importClass(fqClassName
135: .replace('$', '.'));
136:
137: // Give the static space its class static import
138: // important to do this after all classes are defined
139: classStaticNameSpace.setClassStatic(clas);
140:
141: // evaluate the static portion of the block in the static space
142: block.evalBlock(callstack, interpreter, true/*override*/,
143: ClassNodeFilter.CLASSSTATIC);
144:
145: if (!clas.isInterface())
146: installStaticBlock(clas, bshStaticFieldName,
147: classStaticNameSpace, interpreter);
148: }
149:
150: callstack.pop();
151:
152: bcm.doneDefiningClass(fqClassName);
153: return clas;
154: }
155:
156: private static void installStaticBlock(Class genClass,
157: String bshStaticFieldName, NameSpace classStaticNameSpace,
158: Interpreter interpreter) {
159: // Set the static bsh This callback
160: try {
161: LHS lhs = Reflect.getLHSStaticField(genClass,
162: bshStaticFieldName);
163: lhs
164: .assign(classStaticNameSpace.getThis(interpreter),
165: false/*strict*/);
166: } catch (Exception e) {
167: throw new InterpreterError("Error in class gen setup: " + e);
168: }
169: }
170:
171: private static void saveClasses(String className, byte[] code) {
172: String dir = Interpreter.getSaveClassesDir();
173: if (dir != null)
174: try {
175: FileOutputStream out = new FileOutputStream(dir + "/"
176: + className + ".class");
177: out.write(code);
178: out.close();
179: } catch (IOException e) {
180: e.printStackTrace();
181: }
182: }
183:
184: static Variable[] getDeclaredVariables(BSHBlock body,
185: CallStack callstack, Interpreter interpreter,
186: String defaultPackage) {
187: List vars = new ArrayList();
188: for (int child = 0; child < body.jjtGetNumChildren(); child++) {
189: SimpleNode node = (SimpleNode) body.jjtGetChild(child);
190: if (node instanceof BSHTypedVariableDeclaration) {
191: BSHTypedVariableDeclaration tvd = (BSHTypedVariableDeclaration) node;
192: Modifiers modifiers = tvd.modifiers;
193:
194: String type = tvd.getTypeDescriptor(callstack,
195: interpreter, defaultPackage);
196:
197: BSHVariableDeclarator[] vardec = tvd.getDeclarators();
198: for (int i = 0; i < vardec.length; i++) {
199: String name = vardec[i].name;
200: try {
201: Variable var = new Variable(name, type,
202: null/*value*/, modifiers);
203: vars.add(var);
204: } catch (UtilEvalError e) {
205: // value error shouldn't happen
206: }
207: }
208: }
209: }
210:
211: return (Variable[]) vars.toArray(new Variable[0]);
212: }
213:
214: static DelayedEvalBshMethod[] getDeclaredMethods(BSHBlock body,
215: CallStack callstack, Interpreter interpreter,
216: String defaultPackage) {
217: List methods = new ArrayList();
218: for (int child = 0; child < body.jjtGetNumChildren(); child++) {
219: SimpleNode node = (SimpleNode) body.jjtGetChild(child);
220: if (node instanceof BSHMethodDeclaration) {
221: BSHMethodDeclaration md = (BSHMethodDeclaration) node;
222: md.insureNodesParsed();
223: Modifiers modifiers = md.modifiers;
224: String name = md.name;
225: String returnType = md.getReturnTypeDescriptor(
226: callstack, interpreter, defaultPackage);
227: BSHReturnType returnTypeNode = md.getReturnTypeNode();
228: BSHFormalParameters paramTypesNode = md.paramsNode;
229: String[] paramTypes = paramTypesNode
230: .getTypeDescriptors(callstack, interpreter,
231: defaultPackage);
232:
233: DelayedEvalBshMethod bm = new DelayedEvalBshMethod(
234: name, returnType, returnTypeNode, md.paramsNode
235: .getParamNames(), paramTypes,
236: paramTypesNode, md.blockNode,
237: null/*declaringNameSpace*/, modifiers,
238: callstack, interpreter);
239:
240: methods.add(bm);
241: }
242: }
243:
244: return (DelayedEvalBshMethod[]) methods
245: .toArray(new DelayedEvalBshMethod[0]);
246: }
247:
248: /**
249: A node filter that filters nodes for either a class body static
250: initializer or instance initializer. In the static case only static
251: members are passed, etc.
252: */
253: static class ClassNodeFilter implements BSHBlock.NodeFilter {
254: public static final int STATIC = 0, INSTANCE = 1, CLASSES = 2;
255:
256: public static ClassNodeFilter CLASSSTATIC = new ClassNodeFilter(
257: STATIC);
258: public static ClassNodeFilter CLASSINSTANCE = new ClassNodeFilter(
259: INSTANCE);
260: public static ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(
261: CLASSES);
262:
263: int context;
264:
265: private ClassNodeFilter(int context) {
266: this .context = context;
267: }
268:
269: public boolean isVisible(SimpleNode node) {
270: if (context == CLASSES)
271: return node instanceof BSHClassDeclaration;
272:
273: // Only show class decs in CLASSES
274: if (node instanceof BSHClassDeclaration)
275: return false;
276:
277: if (context == STATIC)
278: return isStatic(node);
279:
280: if (context == INSTANCE)
281: return !isStatic(node);
282:
283: // ALL
284: return true;
285: }
286:
287: boolean isStatic(SimpleNode node) {
288: if (node instanceof BSHTypedVariableDeclaration)
289: return ((BSHTypedVariableDeclaration) node).modifiers != null
290: && ((BSHTypedVariableDeclaration) node).modifiers
291: .hasModifier("static");
292:
293: if (node instanceof BSHMethodDeclaration)
294: return ((BSHMethodDeclaration) node).modifiers != null
295: && ((BSHMethodDeclaration) node).modifiers
296: .hasModifier("static");
297:
298: // need to add static block here
299: if (node instanceof BSHBlock)
300: return ((BSHBlock) node).isStatic;
301:
302: return false;
303: }
304: }
305:
306: public static Object invokeSuperclassMethodImpl(
307: BshClassManager bcm, Object instance, String methodName,
308: Object[] args) throws UtilEvalError, ReflectError,
309: InvocationTargetException {
310: String super Name = ClassGeneratorUtil.BSHSUPER + methodName;
311:
312: // look for the specially named super delegate method
313: Class clas = instance.getClass();
314: Method super Method = Reflect.resolveJavaMethod(bcm, clas,
315: super Name, Types.getTypes(args), false/*onlyStatic*/);
316: if (super Method != null)
317: return Reflect.invokeMethod(super Method, instance, args);
318:
319: // No super method, try to invoke regular method
320: // could be a superfluous "super." which is legal.
321: Class super Class = clas.getSuperclass();
322: super Method = Reflect
323: .resolveExpectedJavaMethod(bcm, super Class, instance,
324: methodName, args, false/*onlyStatic*/);
325: return Reflect.invokeMethod(superMethod, instance, args);
326: }
327:
328: }
|