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

prompto.java.JavaMethodExpression Maven / Gradle / Ivy

The newest version!
package prompto.java;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

import prompto.compiler.CompilerException;
import prompto.compiler.Descriptor;
import prompto.compiler.IConstantOperand;
import prompto.compiler.InterfaceConstant;
import prompto.compiler.MethodConstant;
import prompto.compiler.MethodInfo;
import prompto.compiler.Opcode;
import prompto.compiler.PromptoClassLoader;
import prompto.compiler.NamedType;
import prompto.compiler.ResultInfo;
import prompto.declaration.IDeclaration;
import prompto.declaration.IMethodDeclaration;
import prompto.declaration.NativeCategoryDeclaration;
import prompto.error.PromptoError;
import prompto.error.SyntaxError;
import prompto.expression.IExpression;
import prompto.runtime.Context;
import prompto.type.CategoryType;
import prompto.type.IType;
import prompto.type.MethodType;
import prompto.type.VoidType;
import prompto.utils.CodeWriter;
import prompto.value.IValue;
import prompto.value.NativeCategory;


public class JavaMethodExpression extends JavaSelectorExpression {

	String name;
	JavaExpressionList arguments;
	
	public JavaMethodExpression(String name, JavaExpressionList arguments) {
		this.name = name;
		this.arguments = arguments!=null ? arguments : new JavaExpressionList();
	}

	@Override
	public void toDialect(CodeWriter writer) {
		parent.toDialect(writer);
		writer.append('.');
		writer.append(name);
		writer.append('(');
		arguments.toDialect(writer);
		writer.append(')');
	}
	
	@Override
	public String toString() {
		return parent.toString() + "." + name + "(" + arguments.toString() + ")";
	}
	
	@Override
	public IType check(Context context) {
		try {
			Method method = findMethod(context);
			if(method==null) {
				context.getProblemListener().reportUnknownMethod(this, name);
				return VoidType.instance();
			} else
				return new JavaClassType(method.getGenericReturnType());
		} catch(ClassNotFoundException | IOException e) {
			throw new SyntaxError(e.getMessage());
		}
	}
	
	@Override
	public ResultInfo compile(Context context, MethodInfo method) {
		try {
			// push instance if any
			ResultInfo parentType = parent.compile(context, method); 
			// locate method
			Method toCall = findMethod(context, parentType.getType());
			// push arguments if any
			for(int i=0;i argType = toCall.getParameterTypes()[i];
				JavaValueConverter.compileAutoboxing(method, pushed, argType);
			}
			// write method call
			Descriptor.Method dm = new Descriptor.Method(toCall.getParameterTypes(), toCall.getReturnType());
			if(parentType.isInterface()) {
				IConstantOperand operand = new InterfaceConstant(parentType.getType(), toCall.getName(), dm);
				method.addInstruction(Opcode.INVOKEINTERFACE, operand);
			} else {
				IConstantOperand operand = new MethodConstant(parentType.getType(), toCall.getName(), dm);
				if(parentType.isStatic())
					method.addInstruction(Opcode.INVOKESTATIC, operand);
				else
					method.addInstruction(Opcode.INVOKEVIRTUAL, operand);
			}
			return new ResultInfo(toCall.getReturnType());
		} catch(ClassNotFoundException | IOException e) {
			throw new CompilerException(e);
		}
	}
	
	@Override
	public Object interpret(Context context) throws PromptoError {
		Object instance = parent.interpret(context);
		if(instance==null)
			throw new SyntaxError("Could not locate: " + parent.toString());
		if(instance instanceof NativeCategory)
			instance = ((NativeCategory)instance).getInstance();
		try {
			Method method = findMethod(context, instance);
			if(method==null)
				throw new SyntaxError("Could not locate: " + this.toString());
			Object[] args = interpret_arguments(context, method);
			Class klass = instance instanceof Class ? (Class)instance : instance.getClass(); 
			if(klass==instance)
				instance = null;
			try {
				return method.invoke(instance, args);
			} catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
				throw new RuntimeException(e);
			}
		} catch(ClassNotFoundException | IOException e) {
			throw new InternalError(e);
		}
	}
	
	Object[] interpret_arguments(Context context, Method method) throws PromptoError {
		Object[] args = new Object[arguments.size()];
		Class[] types = method.getParameterTypes();
		for(int i=0;i type) throws PromptoError {
        Object value = expression.interpret(context);
        if (value instanceof IExpression)
            value = ((IExpression)value).interpret(context);
        if (value instanceof IValue)
            value = ((IValue)value).toJavaValue(context, type);
        return value;
    }

	public Method findMethod(Context context) throws ClassNotFoundException, IOException {
		IType type = parent.check(context);
		if(type==null) {
			context.getProblemListener().reportUnknownIdentifier(parent, parent.toString());
			return null;
		} else {
			Type klass = findClass(context, type);
			return findMethod(context, klass);
		}
	}
	
	private Type findClass(Context context, IType type) {
		if(type instanceof CategoryType) {
			IDeclaration named = context.getRegisteredDeclaration(IDeclaration.class, type.getTypeNameId());
			if(named instanceof NativeCategoryDeclaration) 
				return ((NativeCategoryDeclaration)named).getBoundClass(true);
		}
		return type.toJavaType(context);
	}

	@SuppressWarnings("resource")
	public Method findMethod(Context context, Object instance) throws ClassNotFoundException, IOException {
		ClassLoader loader = PromptoClassLoader.getInstance();
		if(instance instanceof NamedType)
			instance = Class.forName(((NamedType)instance).getTypeName(), true, loader);
		if(instance instanceof Class)
			return findMethod(context, (Class)instance);
		else
			return findMethod(context, instance.getClass());
	}
	
	public Method findMethod(Context context, Class klass) {
		if(klass==null)
			return null;
		Method method = findExactMethod(context, klass);
		if(method!=null)
			return method;
		else
			return findCompatibleMethod(context, klass);
	}
	
	private Method findExactMethod(Context context, Class klass) {
		Class[] types = new Class[arguments.size()];
		int i = 0;
		try {
			for(JavaExpression exp  : arguments) {
				Type argType = exp.check(context).toJavaType(context);
				if(argType instanceof NamedType)
					argType = Class.forName(argType.getTypeName());
				types[i++] = (Class)argType;
			}
			return klass.getDeclaredMethod(name, types);
		} catch (NoSuchMethodException | ClassNotFoundException e) {
			return null;
		}
	}

	private Method findCompatibleMethod(Context context, Class klass) {
		Method[] methods = klass.getMethods();
		for(Method m : methods) {
			if(!name.equals(m.getName())) 
				continue;
			if(hasValidPrototype(context, m))
				return m;
		}
		return null; 
	}

	boolean hasValidPrototype(Context context,Method method) {
		Class[] types = method.getParameterTypes();
		if(types.length!=arguments.size())
			return false;
		for(int i=0;i klass, JavaExpression argument) {
		if(klass==Object.class)
			return true;
		IType argIType = argument.check(context);
		if(argIType instanceof MethodType && klass==IMethodDeclaration.class) {
			return true;
		} else {
			Type argType = argIType.toJavaType(context);
			if(argType instanceof NamedType) try {
				argType = Class.forName(argType.getTypeName());
			} catch (ClassNotFoundException e) {
				return false;
			}
			return isCompatibleArgument(klass, (Class)argType); // 
		}
	}

	boolean isCompatibleArgument(Class required, Class provided) {
		return required==provided
			|| required.isAssignableFrom((Class)provided)
			|| JavaValueConverter.canBeAutoboxed(required, provided);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy