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