<commit_before>package hu.mentlerd.hybrid.asm;

import hu.mentlerd.hybrid.CallFrame;
import hu.mentlerd.hybrid.LuaTable;
import hu.mentlerd.hybrid.asm.OverloadResolver.OverloadRule;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;

import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Type.*;

public class CoercionAdapter extends GeneratorAdapter{
	
	protected static final String TABLE		= AsmHelper.getAsmName( LuaTable.class );	
	protected static final String FRAME		= AsmHelper.getAsmName( CallFrame.class );

	protected static final Type OBJ_BOOLEAN		= Type.getType( Boolean.class );
	protected static final Type OBJ_DOUBLE		= Type.getType( Double.class );
	
	protected static final Type OBJ_TABLE		= Type.getType( LuaTable.class );
	protected static final Type OBJ_OBJECT		= Type.getType( Object.class );
	
	protected static final Type EXCEPTION		= Type.getType( IllegalArgumentException.class );

	protected static Map<String, Type> numberCoercionMap = new HashMap<String, Type>();
	
	static{
		numberCoercionMap.put("java/lang/Byte",		BYTE_TYPE);
		numberCoercionMap.put("java/lang/Short",	SHORT_TYPE);
		numberCoercionMap.put("java/lang/Integer",	INT_TYPE);
		numberCoercionMap.put("java/lang/Float",	FLOAT_TYPE);
		numberCoercionMap.put("java/lang/Long", 	LONG_TYPE);
	}
	
	//This method is here for nested tables: [[I -> [I
	private static Type getEntryType( Type array ){
		return Type.getType( array.getInternalName().substring(1) );
	}
	
	
	/**
	 * Tells if a null value can be coerced into the target class
	 * 
	 * (Only returns true on non boxed objects, apart from Boolean,
	 * that is evaluated like the Lua standard)
	 * 
	 * @param into The target class
	 * @return Whether it is possible to coerce a null value to the target class
	 */
	public static boolean canCoerceNull( Type into ){
		if ( into.getSort() == Type.OBJECT ){
			String clazz = into.getInternalName();
			
			if ( numberCoercionMap.containsKey(clazz) )
				return false;
			
			if ( clazz.equals(CHAR) )
				return false;
			
			return true;
		}
		
		return false;
	}
	
	/**
	 * Returns the type required to be on the stack to coerce it into the target type.
	 * 
	 * @param type The target type
	 * @return The type required on the stack
	 */
	public static Type getCoercedType( Type type ){	
		switch( type.getSort() ){
			
			//Primitives
			case Type.BOOLEAN:	
				return OBJ_BOOLEAN;
			
			//Java numbers are coerced from Double objects
			case Type.BYTE:		
			case Type.CHAR:
			case Type.SHORT:
			case Type.INT:
			
			case Type.FLOAT:
			case Type.LONG:
			
			case Type.DOUBLE:
				return OBJ_DOUBLE;
			
			case Type.OBJECT:
				String clazzName = type.getInternalName();

				//Non primitive numbers object are coerced from Double objects
				if ( numberCoercionMap.containsKey(clazzName) )
					return OBJ_DOUBLE;
				
				return type;
			
			//Arrays are coerced from LuaTables
			case Type.ARRAY:
				return OBJ_TABLE;
				
			default:
				throw new IllegalArgumentException();
		}
	}

	/**
	 * Tells whether the method passes is a valid @LuaMethod
	 * 
	 * (Its return type is int, and only the last parameter is a CallFrame)
	 * @param method
	 * @return
	 */
	public static final boolean isMethodSpecial( Method method ){
		if ( !method.isAnnotationPresent( LuaMethod.class ) || method.isVarArgs() )
			return false;
		
		Type[] rTypes	= Type.getArgumentTypes(method);
		Type rType		= Type.getReturnType(method);
		
		//Check for integer return type
		if ( rType != Type.INT_TYPE )	
			return false;
		
		//Check for CallFrame parameter
		int params = rTypes.length;
		
		if ( params == 0 )
			return false;
		
		Type last = rTypes[params -1];
		
		if ( last.getSort() != Type.OBJECT || !last.getInternalName().equals(FRAME) )
			return false;
		
		return true;
	}

	
	//Instance
	protected int frameArgIndex = -1;
	
	public CoercionAdapter(MethodVisitor mv, int access, String name, String desc) {
		super(mv, access, name, desc);
	}
	public CoercionAdapter(ClassVisitor cw, int access, String name, String desc){	
		super(cw.visitMethod(access, name, desc, null, null), access, name, desc);
	}
	
	//Utility
	public void push0(){ visitInsn(ICONST_0); }
	public void push1(){ visitInsn(ICONST_1); }
	
	private void visitIfValid(Label label) {
		if ( label != null )
			visitLabel(label);
	}

	//Java calls
	public void callJava( Type clazz, Method method ){
		boolean isStatic	= AsmHelper.isStatic(method);
		
		boolean isSpecial	= isMethodSpecial(method);
		boolean isVararg	= method.isVarArgs();
		
		int callType		= INVOKESTATIC;

		String owner	= clazz.getInternalName();
		Type pTypes[]	= Type.getArgumentTypes(method);
		
		//Non static calls require an instance, and INVOKEVIRTUAL
		if ( !isStatic ){
			pushSelfArg(clazz);
			callType = INVOKEVIRTUAL;
			
			if ( AsmHelper.isInterfaceMethod(method) )
				callType = INVOKEINTERFACE;
		}
		
		//Extract parameters to the stack
		int params 	= pTypes.length;
		int off		= 1;
		
		if ( isVararg || isSpecial ) //Last parameter is handled differently
			params -= 1;
		
		if ( isStatic ) //No instance required for static methods 
			off -= 1;
		
		//Extract parameters
		for ( int index = 0; index < params; index++ )
			coerceFrameArg(index + off, pTypes[index]);
	
		//Extract varargs
		if ( isVararg )
			coerceFrameVarargs(params, pTypes[params]);
		
		//Push the callframe
		if ( isSpecial )
			loadArg(frameArgIndex);
		
		//Call
		visitMethodInsn(callType, owner, method.getName(), Type.getMethodDescriptor(method));
		
		if ( isSpecial ) //Special methods manage returning themselves
			return;
		
		//Check if there are arguments to return
		Type rType = Type.getReturnType(method);
		
		if ( rType != Type.VOID_TYPE ) {
			varToLua(rType); //Coerce return, and push on CallFrame
			popToFrame();
			
			push1();
		} else {
			push0();
		}	
	}

	public void callOverload( Type clazz, List<Method> methods ){
		List<OverloadRule> rules = OverloadResolver.resolve(methods);
		
		OverloadRule lastRule = rules.get( rules.size() -1 );
		
		boolean isStatic = AsmHelper.isStatic( lastRule.method );
		
		//Start processing rules, and storing variables
		int lastParamCount 		= -1;
		
		Label nextArgBranch		= null;
		Label nextValueBranch	= null;
		
		Label finish			= new Label();
		
		//Extract parameter count
		int argCount = newLocal(Type.INT_TYPE);
		
		//Extract frame.getArgCount
		pushFrameArgCount();
		storeLocal(argCount);

		//Create branches based off rules
		for ( OverloadRule rule : rules ){
			int paramCount = rule.paramCount;
			
			if ( lastParamCount != paramCount ){ //Argument count mismatch, new branch
				visitIfValid(nextArgBranch);
				nextArgBranch = new Label();<commit_msg>Fix overloaded instance methods not working<commit_after>package hu.mentlerd.hybrid.asm;

import hu.mentlerd.hybrid.CallFrame;
import hu.mentlerd.hybrid.LuaTable;
import hu.mentlerd.hybrid.asm.OverloadResolver.OverloadRule;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;

import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.Type.*;

public class CoercionAdapter extends GeneratorAdapter{
	
	protected static final String TABLE		= AsmHelper.getAsmName( LuaTable.class );	
	protected static final String FRAME		= AsmHelper.getAsmName( CallFrame.class );

	protected static final Type OBJ_BOOLEAN		= Type.getType( Boolean.class );
	protected static final Type OBJ_DOUBLE		= Type.getType( Double.class );
	
	protected static final Type OBJ_TABLE		= Type.getType( LuaTable.class );
	protected static final Type OBJ_OBJECT		= Type.getType( Object.class );
	
	protected static final Type EXCEPTION		= Type.getType( IllegalArgumentException.class );

	protected static Map<String, Type> numberCoercionMap = new HashMap<String, Type>();
	
	static{
		numberCoercionMap.put("java/lang/Byte",		BYTE_TYPE);
		numberCoercionMap.put("java/lang/Short",	SHORT_TYPE);
		numberCoercionMap.put("java/lang/Integer",	INT_TYPE);
		numberCoercionMap.put("java/lang/Float",	FLOAT_TYPE);
		numberCoercionMap.put("java/lang/Long", 	LONG_TYPE);
	}
	
	//This method is here for nested tables: [[I -> [I
	private static Type getEntryType( Type array ){
		return Type.getType( array.getInternalName().substring(1) );
	}
	
	
	/**
	 * Tells if a null value can be coerced into the target class
	 * 
	 * (Only returns true on non boxed objects, apart from Boolean,
	 * that is evaluated like the Lua standard)
	 * 
	 * @param into The target class
	 * @return Whether it is possible to coerce a null value to the target class
	 */
	public static boolean canCoerceNull( Type into ){
		if ( into.getSort() == Type.OBJECT ){
			String clazz = into.getInternalName();
			
			if ( numberCoercionMap.containsKey(clazz) )
				return false;
			
			if ( clazz.equals(CHAR) )
				return false;
			
			return true;
		}
		
		return false;
	}
	
	/**
	 * Returns the type required to be on the stack to coerce it into the target type.
	 * 
	 * @param type The target type
	 * @return The type required on the stack
	 */
	public static Type getCoercedType( Type type ){	
		switch( type.getSort() ){
			
			//Primitives
			case Type.BOOLEAN:	
				return OBJ_BOOLEAN;
			
			//Java numbers are coerced from Double objects
			case Type.BYTE:		
			case Type.CHAR:
			case Type.SHORT:
			case Type.INT:
			
			case Type.FLOAT:
			case Type.LONG:
			
			case Type.DOUBLE:
				return OBJ_DOUBLE;
			
			case Type.OBJECT:
				String clazzName = type.getInternalName();

				//Non primitive numbers object are coerced from Double objects
				if ( numberCoercionMap.containsKey(clazzName) )
					return OBJ_DOUBLE;
				
				return type;
			
			//Arrays are coerced from LuaTables
			case Type.ARRAY:
				return OBJ_TABLE;
				
			default:
				throw new IllegalArgumentException();
		}
	}

	/**
	 * Tells whether the method passes is a valid @LuaMethod
	 * 
	 * (Its return type is int, and only the last parameter is a CallFrame)
	 * @param method
	 * @return
	 */
	public static final boolean isMethodSpecial( Method method ){
		if ( !method.isAnnotationPresent( LuaMethod.class ) || method.isVarArgs() )
			return false;
		
		Type[] rTypes	= Type.getArgumentTypes(method);
		Type rType		= Type.getReturnType(method);
		
		//Check for integer return type
		if ( rType != Type.INT_TYPE )	
			return false;
		
		//Check for CallFrame parameter
		int params = rTypes.length;
		
		if ( params == 0 )
			return false;
		
		Type last = rTypes[params -1];
		
		if ( last.getSort() != Type.OBJECT || !last.getInternalName().equals(FRAME) )
			return false;
		
		return true;
	}

	
	//Instance
	protected int frameArgIndex = -1;
	
	public CoercionAdapter(MethodVisitor mv, int access, String name, String desc) {
		super(mv, access, name, desc);
	}
	public CoercionAdapter(ClassVisitor cw, int access, String name, String desc){	
		super(cw.visitMethod(access, name, desc, null, null), access, name, desc);
	}
	
	//Utility
	public void push0(){ visitInsn(ICONST_0); }
	public void push1(){ visitInsn(ICONST_1); }
	
	private void visitIfValid(Label label) {
		if ( label != null )
			visitLabel(label);
	}

	//Java calls
	public void callJava( Type clazz, Method method ){
		boolean isStatic	= AsmHelper.isStatic(method);
		
		boolean isSpecial	= isMethodSpecial(method);
		boolean isVararg	= method.isVarArgs();
		
		int callType		= INVOKESTATIC;

		String owner	= clazz.getInternalName();
		Type pTypes[]	= Type.getArgumentTypes(method);
		
		//Non static calls require an instance, and INVOKEVIRTUAL
		if ( !isStatic ){
			pushSelfArg(clazz);
			callType = INVOKEVIRTUAL;
			
			if ( AsmHelper.isInterfaceMethod(method) )
				callType = INVOKEINTERFACE;
		}
		
		//Extract parameters to the stack
		int params 	= pTypes.length;
		int off		= 1;
		
		if ( isVararg || isSpecial ) //Last parameter is handled differently
			params -= 1;
		
		if ( isStatic ) //No instance required for static methods 
			off -= 1;
		
		//Extract parameters
		for ( int index = 0; index < params; index++ )
			coerceFrameArg(index + off, pTypes[index]);
	
		//Extract varargs
		if ( isVararg )
			coerceFrameVarargs(params, pTypes[params]);
		
		//Push the callframe
		if ( isSpecial )
			loadArg(frameArgIndex);
		
		//Call
		visitMethodInsn(callType, owner, method.getName(), Type.getMethodDescriptor(method));
		
		if ( isSpecial ) //Special methods manage returning themselves
			return;
		
		//Check if there are arguments to return
		Type rType = Type.getReturnType(method);
		
		if ( rType != Type.VOID_TYPE ) {
			varToLua(rType); //Coerce return, and push on CallFrame
			popToFrame();
			
			push1();
		} else {
			push0();
		}	
	}

	public void callOverload( Type clazz, List<Method> methods ){
		List<OverloadRule> rules = OverloadResolver.resolve(methods);
		
		OverloadRule lastRule = rules.get( rules.size() -1 );
		
		boolean isStatic = AsmHelper.isStatic( lastRule.method );
		
		//Start processing rules, and storing variables
		int lastParamCount 		= -1;
		
		Label nextArgBranch		= null;
		Label nextValueBranch	= null;
		
		Label finish			= new Label();
		
		//Extract parameter count
		int argCount = newLocal(Type.INT_TYPE);
		
		//Extract frame.getArgCount
		pushFrameArgCount();

		if ( !isStatic ){
			push1();
			math(SUB, INT_TYPE);
		}
		
		storeLocal(argCount);

		//Create branches based off rules
		for ( OverloadRule rule : rules ){
			int paramCount = rule.paramCount;
			
			if ( lastParamCount != paramCount ){ //Argument count mismatch, new branch
				visitIfValid(nextArgBranch);
				nextArgBranch = new Label();
{'fraction_non_alphanumeric': 0.10183066361556065, 'fraction_numerical': 0.0023598398169336387, 'mean_word_length': 3.422833649588868, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7051251', 'n_tokens_mistral': 5107, 'n_tokens_neox': 4348, 'n_words': 1269}