All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bsh.ClassGeneratorImpl Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * The AIBench Shell Plugin
 * %%
 * Copyright (C) 2006 - 2017 Daniel Glez-Peña and Florentino Fdez-Riverola
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
package bsh;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @author Pat Niemeyer ([email protected])
 */
public class ClassGeneratorImpl extends ClassGenerator {
	@Override
	public Class generateClass(
		String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, boolean isInterface, CallStack callstack,
		Interpreter interpreter
	) throws EvalError {
		// Delegate to the static method
		return generateClassImpl(name, modifiers, interfaces, superClass, block, isInterface, callstack, interpreter);
	}

	@Override
	public Object invokeSuperclassMethod(BshClassManager bcm, Object instance, String methodName, Object[] args)
		throws UtilEvalError, ReflectError, InvocationTargetException {
		// Delegate to the static method
		return invokeSuperclassMethodImpl(bcm, instance, methodName, args);
	}

	/**
	 * Change the parent of the class instance namespace. This is currently used
	 * for inner class support. Note: This method will likely be removed in the
	 * future.
	 * 
	 * @param instance the instance whose parent will be changed.
	 * @param className the class name.
	 * @param parent the new parent.
	 */
	// This could be static
	@Override
	public void setInstanceNameSpaceParent(Object instance, String className, NameSpace parent) {
		This ithis = ClassGeneratorUtil.getClassInstanceThis(instance, className);
		ithis.getNameSpace().setParent(parent);
	}

	/**
	 * Parse the BSHBlock for for the class definition and generate the class
	 * using ClassGenerator.
	 * 
	 * @param name the name of the class to be generated.
	 * @param modifiers the class modifiers.
	 * @param interfaces the interfaces implemented by the class.
	 * @param superClass the super class of the generated class.
	 * @param block the BeanShell code block related.
	 * @param isInterface whether the class is an interface or not.
	 * @param callstack the callstack related.
	 * @param interpreter the interpreter.
	 * @return a new generated class.
	 * @throws EvalError if an error occurs while generating the class.
	 */
	public static Class generateClassImpl(
		String name, Modifiers modifiers, Class[] interfaces, Class superClass, BSHBlock block, boolean isInterface, CallStack callstack,
		Interpreter interpreter
	) throws EvalError {
		// Scripting classes currently requires accessibility
		// This can be eliminated with a bit more work.
		try {
			Capabilities.setAccessibility(true);
		} catch (Capabilities.Unavailable e) {
			throw new EvalError("Defining classes currently requires reflective Accessibility.", block, callstack);
		}

		NameSpace enclosingNameSpace = callstack.top();
		String packageName = enclosingNameSpace.getPackage();
		String className = enclosingNameSpace.isClass ? (enclosingNameSpace.getName() + "$" + name) : name;
		String fqClassName = packageName == null ? className : packageName + "." + className;

		BshClassManager bcm = interpreter.getClassManager();
		// Race condition here...
		bcm.definingClass(fqClassName);

		// Create the class static namespace
		NameSpace classStaticNameSpace = new NameSpace(enclosingNameSpace, className);
		classStaticNameSpace.isClass = true;

		callstack.push(classStaticNameSpace);

		// Evaluate any inner class class definitions in the block
		// effectively recursively call this method for contained
		// classes first
		block.evalBlock(callstack, interpreter, true/* override */, ClassNodeFilter.CLASSCLASSES);

		// Generate the type for our class
		Variable[] variables = getDeclaredVariables(block, callstack, interpreter, packageName);
		DelayedEvalBshMethod[] methods = getDeclaredMethods(block, callstack, interpreter, packageName);

		ClassGeneratorUtil classGenerator = new ClassGeneratorUtil(
			modifiers, className, packageName, superClass, interfaces, variables, methods, classStaticNameSpace, isInterface
		);
		byte[] code = classGenerator.generateClass();

		// if debug, write out the class file to debugClasses directory
		String dir = System.getProperty("debugClasses");
		if (dir != null)
			try {
				FileOutputStream out = new FileOutputStream(dir + "/" + className + ".class");
				out.write(code);
				out.close();
			} catch (IOException e) {}

		// Define the new class in the classloader
		Class genClass = bcm.defineClass(fqClassName, code);

		// import the unq name into parent
		enclosingNameSpace.importClass(fqClassName.replace('$', '.'));

		try {
			classStaticNameSpace.setLocalVariable(ClassGeneratorUtil.BSHINIT, block, false/* strictJava */);
		} catch (UtilEvalError e) {
			throw new InterpreterError("unable to init static: " + e);
		}

		// Give the static space its class static import
		// important to do this after all classes are defined
		classStaticNameSpace.setClassStatic(genClass);

		// evaluate the static portion of the block in the static space
		block.evalBlock(callstack, interpreter, true/* override */, ClassNodeFilter.CLASSSTATIC);

		callstack.pop();

		if (!genClass.isInterface()) {
			// Set the static bsh This callback
			String bshStaticFieldName = ClassGeneratorUtil.BSHSTATIC + className;
			try {
				LHS lhs = Reflect.getLHSStaticField(genClass, bshStaticFieldName);
				lhs.assign(classStaticNameSpace.getThis(interpreter), false/* strict */);
			} catch (Exception e) {
				throw new InterpreterError("Error in class gen setup: " + e);
			}
		}

		bcm.doneDefiningClass(fqClassName);
		return genClass;
	}

	static Variable[] getDeclaredVariables(BSHBlock body, CallStack callstack, Interpreter interpreter, String defaultPackage) {
		List vars = new ArrayList();
		for (int child = 0; child < body.jjtGetNumChildren(); child++) {
			SimpleNode node = (SimpleNode) body.jjtGetChild(child);
			if (node instanceof BSHTypedVariableDeclaration) {
				BSHTypedVariableDeclaration tvd = (BSHTypedVariableDeclaration) node;
				Modifiers modifiers = tvd.modifiers;

				String type = tvd.getTypeDescriptor(callstack, interpreter, defaultPackage);

				BSHVariableDeclarator[] vardec = tvd.getDeclarators();
				for (int i = 0; i < vardec.length; i++) {
					String name = vardec[i].name;
					try {
						Variable var = new Variable(name, type, null/* value */, modifiers);
						vars.add(var);
					} catch (UtilEvalError e) {
						// value error shouldn't happen
					}
				}
			}
		}

		return (Variable[]) vars.toArray(new Variable[0]);
	}

	static DelayedEvalBshMethod[] getDeclaredMethods(BSHBlock body, CallStack callstack, Interpreter interpreter, String defaultPackage) throws EvalError {
		List methods = new ArrayList();
		for (int child = 0; child < body.jjtGetNumChildren(); child++) {
			SimpleNode node = (SimpleNode) body.jjtGetChild(child);
			if (node instanceof BSHMethodDeclaration) {
				BSHMethodDeclaration md = (BSHMethodDeclaration) node;
				md.insureNodesParsed();
				Modifiers modifiers = md.modifiers;
				String name = md.name;
				String returnType = md.getReturnTypeDescriptor(callstack, interpreter, defaultPackage);
				BSHReturnType returnTypeNode = md.getReturnTypeNode();
				BSHFormalParameters paramTypesNode = md.paramsNode;
				String[] paramTypes = paramTypesNode.getTypeDescriptors(callstack, interpreter, defaultPackage);

				DelayedEvalBshMethod bm = new DelayedEvalBshMethod(
					name, returnType, returnTypeNode, md.paramsNode.getParamNames(), paramTypes, paramTypesNode, md.blockNode, null/* declaringNameSpace */,
					modifiers, callstack, interpreter
				);

				methods.add(bm);
			}
		}

		return (DelayedEvalBshMethod[]) methods.toArray(new DelayedEvalBshMethod[0]);
	}

	/**
	 * A node filter that filters nodes for either a class body static
	 * initializer or instance initializer. In the static case only static
	 * members are passed, etc.
	 */
	static class ClassNodeFilter implements BSHBlock.NodeFilter {
		public static final int STATIC = 0, INSTANCE = 1, CLASSES = 2;

		public static ClassNodeFilter CLASSSTATIC = new ClassNodeFilter(STATIC);

		public static ClassNodeFilter CLASSINSTANCE = new ClassNodeFilter(INSTANCE);

		public static ClassNodeFilter CLASSCLASSES = new ClassNodeFilter(CLASSES);

		int context;

		private ClassNodeFilter(int context) {
			this.context = context;
		}

		public boolean isVisible(SimpleNode node) {
			if (context == CLASSES)
				return node instanceof BSHClassDeclaration;

			// Only show class decs in CLASSES
			if (node instanceof BSHClassDeclaration)
				return false;

			if (context == STATIC)
				return isStatic(node);

			if (context == INSTANCE)
				return !isStatic(node);

			// ALL
			return true;
		}

		boolean isStatic(SimpleNode node) {
			if (node instanceof BSHTypedVariableDeclaration)
				return ((BSHTypedVariableDeclaration) node).modifiers != null && ((BSHTypedVariableDeclaration) node).modifiers.hasModifier("static");

			if (node instanceof BSHMethodDeclaration)
				return ((BSHMethodDeclaration) node).modifiers != null && ((BSHMethodDeclaration) node).modifiers.hasModifier("static");

			// need to add static block here
			if (node instanceof BSHBlock)
				return false;

			return false;
		}
	}

	public static Object invokeSuperclassMethodImpl(BshClassManager bcm, Object instance, String methodName, Object[] args)
		throws UtilEvalError, ReflectError, InvocationTargetException {
		String superName = ClassGeneratorUtil.BSHSUPER + methodName;

		// look for the specially named super delegate method
		Class clas = instance.getClass();
		Method superMethod = Reflect.resolveJavaMethod(bcm, clas, superName, Types.getTypes(args), false/* onlyStatic */);
		if (superMethod != null)
			return Reflect.invokeMethod(superMethod, instance, args);

		// No super method, try to invoke regular method
		// could be a superfluous "super." which is legal.
		Class superClass = clas.getSuperclass();
		superMethod = Reflect.resolveExpectedJavaMethod(bcm, superClass, instance, methodName, args, false/* onlyStatic */);
		return Reflect.invokeMethod(superMethod, instance, args);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy