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

prompto.param.MethodParameter Maven / Gradle / Ivy

The newest version!
package prompto.param;

import java.lang.reflect.Type;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import prompto.compiler.ClassConstant;
import prompto.compiler.ClassFile;
import prompto.compiler.CompilerUtils;
import prompto.compiler.Descriptor;
import prompto.compiler.Flags;
import prompto.compiler.IVerifierEntry.VerifierType;
import prompto.compiler.InterfaceType;
import prompto.compiler.MethodConstant;
import prompto.compiler.MethodInfo;
import prompto.compiler.Opcode;
import prompto.compiler.ResultInfo;
import prompto.compiler.NamedType;
import prompto.compiler.StackLocal;
import prompto.compiler.StringConstant;
import prompto.declaration.IMethodDeclaration;
import prompto.error.PromptoError;
import prompto.error.SyntaxError;
import prompto.expression.ArrowExpression;
import prompto.expression.IExpression;
import prompto.grammar.Argument;
import prompto.grammar.ArgumentList;
import prompto.grammar.INamed;
import prompto.grammar.Identifier;
import prompto.intrinsic.PromptoProxy;
import prompto.parser.Dialect;
import prompto.runtime.Context;
import prompto.runtime.Context.MethodDeclarationMap;
import prompto.transpiler.Transpiler;
import prompto.type.IType;
import prompto.type.MethodType;
import prompto.type.VoidType;
import prompto.utils.CodeWriter;
import prompto.value.ArrowValue;
import prompto.value.ContextualExpression;
import prompto.value.IValue;

public class MethodParameter extends BaseParameter {
	
	MethodType resolved;
	
	public MethodParameter(Identifier id) {
		this(null, id);
	}
	
	public MethodParameter(MethodType resolved, Identifier id) {
		super(id);
		this.resolved = resolved;
	}

	@Override
	public String getSignature(Dialect dialect) {
		return id.toString();
	}
	
	@Override
	public void toDialect(CodeWriter writer) {
		writer.append(id);
	}

	@Override
	public String toString() {
		return id.toString();
	}
	
	@Override
	public String getProto() {
		return id.toString();
	}
	
	@Override
	public int hashCode() {
		return Objects.hash(getId());
	}


	@Override
	public boolean equals(Object obj) {
		if(obj==this)
			return true;
		if(obj==null)
			return false;
		if(!(obj instanceof MethodParameter))
			return false;
		MethodParameter other = (MethodParameter)obj;
		return Objects.equals(this.getId(),other.getId());
	}

	@Override
	public void register(Context context) {
		INamed actual = context.getRegisteredValue(INamed.class,id);
		if(actual!=null)
			throw new SyntaxError("Duplicate parameter: \"" + id + "\"");
		context.registerInstance(this);
	}
	
	@Override
	public IType check(Context context) {
		IMethodDeclaration actual = getDeclaration(context);
		if(actual!=null)
			return actual.check(context);
		context.getProblemListener().reportUnknownMethod(id, id.toString());
		return null;
	}
	
	@Override
	public IValue checkValue(Context context, IExpression expression) throws PromptoError {
		boolean isArrow = expression instanceof ContextualExpression && ((ContextualExpression)expression).getExpression() instanceof ArrowExpression;
		if(isArrow)
			return checkArrowValue(context, (ContextualExpression)expression);
		else
			return super.checkValue(context, expression);
	}
	
	private IValue checkArrowValue(Context context, ContextualExpression expression) {
		return new ArrowValue(getDeclaration(context), expression.getCalling(), (ArrowExpression)expression.getExpression()); // TODO check
	}

	@Override
	public MethodType getType(Context context) {
		resolve(context);
		return resolved;
	}
	
	private void resolve(Context context) {
		if(resolved == null) {
			IMethodDeclaration actual = getDeclaration(context);
			resolved = new MethodType(actual);
		}
	}

	private IMethodDeclaration getDeclaration(Context context) {
		MethodDeclarationMap methods = context.getRegisteredDeclaration(MethodDeclarationMap.class, id);
		if(methods!=null)
			return (IMethodDeclaration)(methods.values().iterator().next());
		else
			return null;
	}
	
	@Override
	public ResultInfo compileParameter(Context context, MethodInfo method, Flags flags, ArgumentList arguments, boolean isFirst) {
		// 1st parameter is method reference
		Argument argument = makeArgument(arguments, isFirst);
		IExpression expression = argument.getExpression();
		if(expression instanceof ArrowExpression)
			return compileArrowArgument(context, method, flags, (ArrowExpression)expression);
		else
			return compileMethodArgument(context, method, flags, expression);
	}
	
	private ResultInfo compileMethodArgument(Context context, MethodInfo method, Flags flags, IExpression expression) {
		resolve(context);
		IMethodDeclaration decl = resolved.getMethod();
		ParameterList parameters = decl.getParameters();
		// the JVM can only cast to declared types, so we need a proxy to convert the FunctionalInterface call into the concrete one
		expression.compileReference(context.getCallingContext(), method, flags); // this would return a lambda
		// what interface we are casting to
		ClassConstant dest = new ClassConstant(getJavaType(context));
		method.addInstruction(Opcode.LDC, dest);
		// method name
		InterfaceType intf = new InterfaceType(parameters, decl.getReturnType());
		String methodName = intf.getInterfaceMethodName(); 
		method.addInstruction(Opcode.LDC, new StringConstant(methodName));
		// method arg types
		List javaTypes = parameters.stream().map(arg->arg.getJavaType(context)).collect(Collectors.toList());
		CompilerUtils.compileClassConstantsArray(method, javaTypes);
		// create proxy
		MethodConstant m = new MethodConstant(PromptoProxy.class, "newProxy", Object.class, Class.class, String.class, Class[].class, Object.class);
		method.addInstruction(Opcode.INVOKESTATIC, m);
		method.addInstruction(Opcode.CHECKCAST, dest);
		return new ResultInfo(dest.getType());
	}

	private ResultInfo compileArrowArgument(Context context, MethodInfo method, Flags flags, ArrowExpression expression) {
		MethodType target = getType(context);
		IMethodDeclaration decl = target.getMethod();
		ParameterList parameters = decl.getParameters();
		InterfaceType intf = new InterfaceType(parameters, decl.getReturnType());
		// create an instance on which the method will be invoked
		String innerClassName = compileArrowExpressionInnerClass(context, method.getClassFile(), intf, expression);
		return compileNewArrowExpressionInstance(context, method, flags, innerClassName, expression);
	}
	
	
	private ResultInfo compileNewArrowExpressionInstance(Context context, MethodInfo method, Flags flags, String innerClassName, ArrowExpression expression) {
		// TODO support closure (see ConcreteMethodDeclaration.compileClosureClass)
		Type innerType = new NamedType(innerClassName); 
		return CompilerUtils.compileNewInstance(method, innerType); 
	}

	private String compileArrowExpressionInnerClass(Context context, ClassFile parentClass, InterfaceType interfaceType, ArrowExpression expression) {
		int innerClassIndex = 1 + parentClass.getInnerClasses().size();
		String innerClassName = parentClass.getThisClass().getType().getTypeName() + '$' + innerClassIndex;
		ClassFile classFile = new ClassFile(new NamedType(innerClassName));
		classFile.setSuperClass(new ClassConstant(Object.class));
		classFile.addInterface(new ClassConstant(interfaceType.getInterfaceType()));
		CompilerUtils.compileEmptyConstructor(classFile);
		compileInnerClassProxyMethod(context, classFile, interfaceType);
		compileInnerClassArrowMethod(context, classFile, expression);
		parentClass.addInnerClass(classFile);
		return innerClassName;
	}

	private void compileInnerClassProxyMethod(Context context, ClassFile classFile, InterfaceType interfaceType) {
		// context = prepareContext(context, isStart);
		MethodType target = getType(context);
		IMethodDeclaration declaration = target.getMethod();
		Type[] paramTypes = declaration.getParameters().stream()
				.filter(a-> 
					!(a instanceof CodeParameter)) // stripOutTemplateArguments
				.map((arg)->
					Object.class)
				.collect(Collectors.toList())
				.toArray(new Type[0]);
		Type returnType = declaration.getReturnType() == VoidType.instance() ? void.class : Object.class;
		Descriptor.Method proto = new Descriptor.Method(paramTypes, returnType);
		MethodInfo method = classFile.newMethod(interfaceType.getInterfaceMethodName(), proto); 
		method.registerLocal("this", VerifierType.ITEM_Object, classFile.getThisClass());	
		declaration.getParameters().forEach(param->{
			ClassConstant classConstant = new ClassConstant(Object.class);
			method.registerLocal(param.getName(), VerifierType.ITEM_Object, classConstant);		
		});
		produceInnerClassProxyByteCode(context, method, declaration);
	}
	
	private void produceInnerClassProxyByteCode(Context context, MethodInfo method, IMethodDeclaration declaration) {
		// calling concrete method with implicit this
		method.addInstruction(Opcode.ALOAD_0, method.getClassFile().getThisClass()); // 'this' is always at index 0
		// push and cast arguments on the stack
		declaration.getParameters().forEach(param->{
			StackLocal local = method.getRegisteredLocal(param.getName());
			CompilerUtils.compileALOAD(method, local);
			Type type = param.getJavaType(context);
			if(type!=Object.class)
				method.addInstruction(Opcode.CHECKCAST, new ClassConstant(type));
		});
		// call virtual method
		Descriptor.Method descriptor = CompilerUtils.createMethodDescriptor(context, declaration.getParameters(), declaration.getReturnType());
		MethodConstant constant = new MethodConstant(method.getClassFile().getThisClass(), declaration.getName(), descriptor);
		method.addInstruction(Opcode.INVOKEVIRTUAL, constant);
		// add return for void
		if(declaration.getReturnType()==VoidType.instance())
			method.addInstruction(Opcode.RETURN);
		else
			method.addInstruction(Opcode.ARETURN);
	}

	private void compileInnerClassArrowMethod(Context context, ClassFile classFile, ArrowExpression expression) {
		// context = prepareContext(context, isStart);
		MethodType target = getType(context);
		IMethodDeclaration declaration = target.getMethod();
		Descriptor.Method proto =CompilerUtils.createMethodDescriptor(context, declaration.getParameters(), declaration.getReturnType());
		MethodInfo method = classFile.newMethod(declaration.getName(), proto); 
		method.registerLocal("this", VerifierType.ITEM_Object, classFile.getThisClass());	
		declaration.getParameters().forEach(param->param.registerLocal(context, method, new Flags()));
		produceInnerClassArrowByteCode(context, method, expression, declaration.getReturnType() == VoidType.instance());
	}

	private void produceInnerClassArrowByteCode(Context context, MethodInfo method, ArrowExpression expression, boolean isVoid) {
		expression.compile(context, method, new Flags());
		// add return for void
		if(isVoid)
			method.addInstruction(Opcode.RETURN);
	}

	@Override
	public void declare(Transpiler transpiler) {
		// nothing to do ?
	}
	
	@Override
	public String getTranspiledName(Context context) {
		IMethodDeclaration method = this.getDeclaration(context);
	    return method.getTranspiledName(context);
	}
	
	@Override
	public void transpileCall(Transpiler transpiler, IExpression expression) {
		if(!transpileArrowExpressionCall(transpiler, expression))
			expression.transpile(transpiler);
	}

	private boolean transpileArrowExpressionCall(Transpiler transpiler, IExpression expression) {
		if(expression instanceof ContextualExpression)
			expression = ((ContextualExpression)expression).getExpression();
		if(expression instanceof ArrowExpression) {
			MethodType target = getType(transpiler.getContext());
			target.transpileArrowExpression(transpiler, (ArrowExpression)expression);
			return true;
		} else
			return false;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy