// SPDX-FileCopyrightText: 2021 deroad <wargio@libero.it>
// SPDX-License-Identifier: LGPL-3.0-only

#include "assembler.h"
#include "const.h"

#define return_error_if_size_lt(a, b) \
	do { \
		if (a < b) { \
			RZ_LOG_ERROR("[!] java_assembler: no enough output buffer (requires %d bytes).\n", b); \
			return false; \
		} \
	} while (0)

#define return_error_if_empty_input(a, b) \
	do { \
		if (RZ_STR_ISEMPTY(a) || b < 1) { \
			RZ_LOG_ERROR("[!] java_assembler: the input is empty.\n"); \
			return false; \
		} \
	} while (0)

typedef bool (*AsmEncoder)(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written);

typedef struct _jasm {
	const char *opcode;
	st32 length;
	ut8 bytecode;
	AsmEncoder encode;
} JavaAsm;

static bool encode_not_implemented(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	RZ_LOG_ERROR("[!] java_assembler: not implemented.\n");
	return false;
}

static bool encode_only_bcode(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 1);

	*written = 1;
	output[0] = bytecode;
	return true;
}

static bool encode_st8(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 2);
	return_error_if_empty_input(input, input_size);

	if (!rz_is_valid_input_num_value(NULL, input)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT8_MIN, INT8_MAX);
		return false;
	}

	*written = 2;
	output[0] = bytecode;
	((st8 *)output)[1] = (st8)strtoll(input, NULL, 0);
	return true;
}

static bool encode_ut8(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 2);
	return_error_if_empty_input(input, input_size);

	if (!rz_is_valid_input_num_value(NULL, input)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT8_MIN, INT8_MAX);
		return false;
	}

	*written = 2;
	output[0] = bytecode;
	output[1] = (ut8)strtoll(input, NULL, 0);
	return true;
}

static bool encode_addr32(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 5);
	return_error_if_empty_input(input, input_size);

	if (!rz_is_valid_input_num_value(NULL, input)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT16_MIN, INT16_MAX);
		return false;
	}

	*written = 5;
	output[0] = bytecode;
	st64 n = strtoll(input, NULL, 0);
	st32 addr = n - pc;
	rz_write_be32(output + 1, addr);
	return true;
}

static bool encode_addr16(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 3);
	return_error_if_empty_input(input, input_size);

	if (!rz_is_valid_input_num_value(NULL, input)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT16_MIN, INT16_MAX);
		return false;
	}

	*written = 3;
	output[0] = bytecode;
	st64 n = strtoll(input, NULL, 0);
	st16 addr = n - pc;
	rz_write_be16(output + 1, addr);
	return true;
}

static bool encode_st16(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 3);
	return_error_if_empty_input(input, input_size);

	if (!rz_is_valid_input_num_value(NULL, input)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between %d and %d (inclusive).\n", input, INT16_MIN, INT16_MAX);
		return false;
	}

	*written = 3;
	output[0] = bytecode;
	st16 n = strtoll(input, NULL, 0);
	rz_write_be16(output + 1, n);
	return true;
}

static bool encode_const_pool8(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 2);
	return_error_if_empty_input(input, input_size);

	ut32 cpool_len = strlen(JAVA_ASM_CONSTANT_POOL_STR);
	if (!strncmp(input, JAVA_ASM_CONSTANT_POOL_STR, cpool_len)) {
		input += cpool_len;
	}

	if (!rz_is_valid_input_num_value(NULL, input)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT8_MAX);
		return false;
	}

	*written = 2;
	output[0] = bytecode;
	output[1] = (ut8)strtoll(input, NULL, 0);
	return true;
}

static bool encode_const_pool16(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 3);
	return_error_if_empty_input(input, input_size);

	ut32 cpool_len = strlen(JAVA_ASM_CONSTANT_POOL_STR);
	if (!strncmp(input, JAVA_ASM_CONSTANT_POOL_STR, cpool_len)) {
		input += cpool_len;
	}

	if (!rz_is_valid_input_num_value(NULL, input)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT16_MAX);
		return false;
	}

	*written = 3;
	output[0] = bytecode;
	ut16 n = (ut16)strtoll(input, NULL, 0);
	rz_write_be16(output + 1, n);
	return true;
}

static bool encode_const_pool16_ut8(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 4);
	return_error_if_empty_input(input, input_size);

	ut32 cpool_len = strlen(JAVA_ASM_CONSTANT_POOL_STR);
	if (!strncmp(input, JAVA_ASM_CONSTANT_POOL_STR, cpool_len)) {
		input += cpool_len;
	}

	if (!rz_is_valid_input_num_value(NULL, input)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT16_MAX);
		return false;
	}

	const char *next = NULL;
	char *tmp = NULL;
	ut16 cpool = (ut16)strtoll(input, &tmp, 0);
	if (!tmp || tmp == (input + input_size) || !(next = rz_str_trim_head_ro(tmp))) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", tmp, UINT8_MAX);
	}

	if (!rz_is_valid_input_num_value(NULL, next)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT8_MAX);
		return false;
	}
	ut8 num = (ut8)strtoll(next, NULL, 0);

	*written = 4;
	output[0] = bytecode;
	rz_write_be16(output + 1, cpool);
	output[3] = num;
	return true;
}

static bool encode_ut8x2(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 3);
	return_error_if_empty_input(input, input_size);

	if (!rz_is_valid_input_num_value(NULL, input)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT8_MAX);
		return false;
	}

	const char *next = NULL;
	char *tmp = NULL;
	ut16 arg0 = (ut16)strtoll(input, &tmp, 0);
	if (!tmp || tmp == (input + input_size) || !(next = rz_str_trim_head_ro(tmp))) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", tmp, UINT8_MAX);
	}

	if (!rz_is_valid_input_num_value(NULL, next)) {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid number between 0 and %u (inclusive).\n", input, UINT8_MAX);
		return false;
	}
	ut8 arg1 = (ut8)strtoll(next, NULL, 0);

	*written = 3;
	output[0] = bytecode;
	output[1] = arg0;
	output[2] = arg1;
	return true;
}

static bool encode_atype(ut8 bytecode, const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	return_error_if_size_lt(output_size, 2);
	return_error_if_empty_input(input, input_size);

	ut8 byte = 0;
	/* bool 4, char 5, float 6, double 7, byte 8, short 9, int 10, long 11 */
	if (!strncmp(input, "bool", strlen("bool"))) {
		byte = 4;
	} else if (!strncmp(input, "char", strlen("char"))) {
		byte = 5;
	} else if (!strncmp(input, "float", strlen("float"))) {
		byte = 6;
	} else if (!strncmp(input, "double", strlen("double"))) {
		byte = 7;
	} else if (!strncmp(input, "byte", strlen("byte"))) {
		byte = 8;
	} else if (!strncmp(input, "short", strlen("short"))) {
		byte = 9;
	} else if (!strncmp(input, "int", strlen("int"))) {
		byte = 10;
	} else if (!strncmp(input, "long", strlen("long"))) {
		byte = 11;
	} else {
		RZ_LOG_ERROR("[!] java_assembler: '%s' is not a valid native type (accepted: bool, char, float, double, byte, short, int, long).\n", input);
		return false;
	}

	*written = 2;
	output[0] = bytecode;
	output[1] = byte;
	return true;
}

#define NS(x) x, (sizeof(x) - 1)
static const JavaAsm instructions[205] = {
	{ NS("wide") /*           */, BYTECODE_C4_WIDE /*           */, encode_only_bcode },
	{ NS("tableswitch") /*    */, BYTECODE_AA_TABLESWITCH /*    */, encode_not_implemented },
	{ NS("swap") /*           */, BYTECODE_5F_SWAP /*           */, encode_only_bcode },
	{ NS("sipush") /*         */, BYTECODE_11_SIPUSH /*         */, encode_st16 },
	{ NS("sastore") /*        */, BYTECODE_56_SASTORE /*        */, encode_only_bcode },
	{ NS("saload") /*         */, BYTECODE_35_SALOAD /*         */, encode_only_bcode },
	{ NS("return") /*         */, BYTECODE_B1_RETURN /*         */, encode_only_bcode },
	{ NS("ret") /*            */, BYTECODE_A9_RET /*            */, encode_ut8 },
	{ NS("putstatic") /*      */, BYTECODE_B3_PUTSTATIC /*      */, encode_const_pool16 },
	{ NS("putfield") /*       */, BYTECODE_B5_PUTFIELD /*       */, encode_const_pool16 },
	{ NS("pop2") /*           */, BYTECODE_58_POP2 /*           */, encode_only_bcode },
	{ NS("pop") /*            */, BYTECODE_57_POP /*            */, encode_only_bcode },
	{ NS("nop") /*            */, BYTECODE_00_NOP /*            */, encode_only_bcode },
	{ NS("newarray") /*       */, BYTECODE_BC_NEWARRAY /*       */, encode_atype },
	{ NS("new") /*            */, BYTECODE_BB_NEW /*            */, encode_const_pool16 },
	{ NS("multianewarray") /* */, BYTECODE_C5_MULTIANEWARRAY /* */, encode_const_pool16_ut8 },
	{ NS("monitorexit") /*    */, BYTECODE_C3_MONITOREXIT /*    */, encode_only_bcode },
	{ NS("monitorenter") /*   */, BYTECODE_C2_MONITORENTER /*   */, encode_only_bcode },
	{ NS("lxor") /*           */, BYTECODE_83_LXOR /*           */, encode_only_bcode },
	{ NS("lushr") /*          */, BYTECODE_7D_LUSHR /*          */, encode_only_bcode },
	{ NS("lsub") /*           */, BYTECODE_65_LSUB /*           */, encode_only_bcode },
	{ NS("lstore_3") /*       */, BYTECODE_42_LSTORE_3 /*       */, encode_only_bcode },
	{ NS("lstore_2") /*       */, BYTECODE_41_LSTORE_2 /*       */, encode_only_bcode },
	{ NS("lstore_1") /*       */, BYTECODE_40_LSTORE_1 /*       */, encode_only_bcode },
	{ NS("lstore_0") /*       */, BYTECODE_3F_LSTORE_0 /*       */, encode_only_bcode },
	{ NS("lstore") /*         */, BYTECODE_37_LSTORE /*         */, encode_ut8 },
	{ NS("lshr") /*           */, BYTECODE_7B_LSHR /*           */, encode_only_bcode },
	{ NS("lshl") /*           */, BYTECODE_79_LSHL /*           */, encode_only_bcode },
	{ NS("lreturn") /*        */, BYTECODE_AD_LRETURN /*        */, encode_only_bcode },
	{ NS("lrem") /*           */, BYTECODE_71_LREM /*           */, encode_only_bcode },
	{ NS("lor") /*            */, BYTECODE_81_LOR /*            */, encode_only_bcode },
	{ NS("lookupswitch") /*   */, BYTECODE_AB_LOOKUPSWITCH /*   */, encode_not_implemented },
	{ NS("lneg") /*           */, BYTECODE_75_LNEG /*           */, encode_only_bcode },
	{ NS("lmul") /*           */, BYTECODE_69_LMUL /*           */, encode_only_bcode },
	{ NS("lload_3") /*        */, BYTECODE_21_LLOAD_3 /*        */, encode_only_bcode },
	{ NS("lload_2") /*        */, BYTECODE_20_LLOAD_2 /*        */, encode_only_bcode },
	{ NS("lload_1") /*        */, BYTECODE_1F_LLOAD_1 /*        */, encode_only_bcode },
	{ NS("lload_0") /*        */, BYTECODE_1E_LLOAD_0 /*        */, encode_only_bcode },
	{ NS("lload") /*          */, BYTECODE_16_LLOAD /*          */, encode_ut8 },
	{ NS("ldiv") /*           */, BYTECODE_6D_LDIV /*           */, encode_only_bcode },
	{ NS("ldc_w") /*          */, BYTECODE_13_LDC_W /*          */, encode_const_pool16 },
	{ NS("ldc2_w") /*         */, BYTECODE_14_LDC2_W /*         */, encode_const_pool16 },
	{ NS("ldc") /*            */, BYTECODE_12_LDC /*            */, encode_const_pool8 },
	{ NS("lconst_1") /*       */, BYTECODE_0A_LCONST_1 /*       */, encode_only_bcode },
	{ NS("lconst_0") /*       */, BYTECODE_09_LCONST_0 /*       */, encode_only_bcode },
	{ NS("lcmp") /*           */, BYTECODE_94_LCMP /*           */, encode_only_bcode },
	{ NS("lastore") /*        */, BYTECODE_50_LASTORE /*        */, encode_only_bcode },
	{ NS("land") /*           */, BYTECODE_7F_LAND /*           */, encode_only_bcode },
	{ NS("laload") /*         */, BYTECODE_2F_LALOAD /*         */, encode_only_bcode },
	{ NS("ladd") /*           */, BYTECODE_61_LADD /*           */, encode_only_bcode },
	{ NS("l2i") /*            */, BYTECODE_88_L2I /*            */, encode_only_bcode },
	{ NS("l2f") /*            */, BYTECODE_89_L2F /*            */, encode_only_bcode },
	{ NS("l2d") /*            */, BYTECODE_8A_L2D /*            */, encode_only_bcode },
	{ NS("jsr_w") /*          */, BYTECODE_C9_JSR_W /*          */, encode_addr32 },
	{ NS("jsr") /*            */, BYTECODE_A8_JSR /*            */, encode_addr16 },
	{ NS("ixor") /*           */, BYTECODE_82_IXOR /*           */, encode_only_bcode },
	{ NS("iushr") /*          */, BYTECODE_7C_IUSHR /*          */, encode_only_bcode },
	{ NS("isub") /*           */, BYTECODE_64_ISUB /*           */, encode_only_bcode },
	{ NS("istore_3") /*       */, BYTECODE_3E_ISTORE_3 /*       */, encode_only_bcode },
	{ NS("istore_2") /*       */, BYTECODE_3D_ISTORE_2 /*       */, encode_only_bcode },
	{ NS("istore_1") /*       */, BYTECODE_3C_ISTORE_1 /*       */, encode_only_bcode },
	{ NS("istore_0") /*       */, BYTECODE_3B_ISTORE_0 /*       */, encode_only_bcode },
	{ NS("istore") /*         */, BYTECODE_36_ISTORE /*         */, encode_ut8 },
	{ NS("ishr") /*           */, BYTECODE_7A_ISHR /*           */, encode_only_bcode },
	{ NS("ishl") /*           */, BYTECODE_78_ISHL /*           */, encode_only_bcode },
	{ NS("ireturn") /*        */, BYTECODE_AC_IRETURN /*        */, encode_only_bcode },
	{ NS("irem") /*           */, BYTECODE_70_IREM /*           */, encode_only_bcode },
	{ NS("ior") /*            */, BYTECODE_80_IOR /*            */, encode_only_bcode },
	{ NS("invokevirtual") /*  */, BYTECODE_B6_INVOKEVIRTUAL /*  */, encode_const_pool16 },
	{ NS("invokestatic") /*   */, BYTECODE_B8_INVOKESTATIC /*   */, encode_const_pool16 },
	{ NS("invokespecial") /*  */, BYTECODE_B7_INVOKESPECIAL /*  */, encode_const_pool16 },
	{ NS("invokeinterface") /**/, BYTECODE_B9_INVOKEINTERFACE /**/, encode_const_pool16_ut8 },
	{ NS("invokedynamic") /*  */, BYTECODE_BA_INVOKEDYNAMIC /*  */, encode_const_pool16_ut8 },
	{ NS("instanceof") /*     */, BYTECODE_C1_INSTANCEOF /*     */, encode_const_pool16 },
	{ NS("ineg") /*           */, BYTECODE_74_INEG /*           */, encode_only_bcode },
	{ NS("imul") /*           */, BYTECODE_68_IMUL /*           */, encode_only_bcode },
	{ NS("impdep2") /*        */, BYTECODE_FF_IMPDEP2 /*        */, encode_only_bcode },
	{ NS("impdep1") /*        */, BYTECODE_FE_IMPDEP1 /*        */, encode_only_bcode },
	{ NS("iload_3") /*        */, BYTECODE_1D_ILOAD_3 /*        */, encode_only_bcode },
	{ NS("iload_2") /*        */, BYTECODE_1C_ILOAD_2 /*        */, encode_only_bcode },
	{ NS("iload_1") /*        */, BYTECODE_1B_ILOAD_1 /*        */, encode_only_bcode },
	{ NS("iload_0") /*        */, BYTECODE_1A_ILOAD_0 /*        */, encode_only_bcode },
	{ NS("iload") /*          */, BYTECODE_15_ILOAD /*          */, encode_ut8 },
	{ NS("iinc") /*           */, BYTECODE_84_IINC /*           */, encode_ut8x2 },
	{ NS("ifnull") /*         */, BYTECODE_C6_IFNULL /*         */, encode_addr16 },
	{ NS("ifnonnull") /*      */, BYTECODE_C7_IFNONNULL /*      */, encode_addr16 },
	{ NS("ifne") /*           */, BYTECODE_9A_IFNE /*           */, encode_addr16 },
	{ NS("iflt") /*           */, BYTECODE_9B_IFLT /*           */, encode_addr16 },
	{ NS("ifle") /*           */, BYTECODE_9E_IFLE /*           */, encode_addr16 },
	{ NS("ifgt") /*           */, BYTECODE_9D_IFGT /*           */, encode_addr16 },
	{ NS("ifge") /*           */, BYTECODE_9C_IFGE /*           */, encode_addr16 },
	{ NS("ifeq") /*           */, BYTECODE_99_IFEQ /*           */, encode_addr16 },
	{ NS("if_icmpne") /*      */, BYTECODE_A0_IF_ICMPNE /*      */, encode_addr16 },
	{ NS("if_icmplt") /*      */, BYTECODE_A1_IF_ICMPLT /*      */, encode_addr16 },
	{ NS("if_icmple") /*      */, BYTECODE_A4_IF_ICMPLE /*      */, encode_addr16 },
	{ NS("if_icmpgt") /*      */, BYTECODE_A3_IF_ICMPGT /*      */, encode_addr16 },
	{ NS("if_icmpge") /*      */, BYTECODE_A2_IF_ICMPGE /*      */, encode_addr16 },
	{ NS("if_icmpeq") /*      */, BYTECODE_9F_IF_ICMPEQ /*      */, encode_addr16 },
	{ NS("if_acmpne") /*      */, BYTECODE_A6_IF_ACMPNE /*      */, encode_addr16 },
	{ NS("if_acmpeq") /*      */, BYTECODE_A5_IF_ACMPEQ /*      */, encode_addr16 },
	{ NS("idiv") /*           */, BYTECODE_6C_IDIV /*           */, encode_only_bcode },
	{ NS("iconst_m1") /*      */, BYTECODE_02_ICONST_M1 /*      */, encode_only_bcode },
	{ NS("iconst_5") /*       */, BYTECODE_08_ICONST_5 /*       */, encode_only_bcode },
	{ NS("iconst_4") /*       */, BYTECODE_07_ICONST_4 /*       */, encode_only_bcode },
	{ NS("iconst_3") /*       */, BYTECODE_06_ICONST_3 /*       */, encode_only_bcode },
	{ NS("iconst_2") /*       */, BYTECODE_05_ICONST_2 /*       */, encode_only_bcode },
	{ NS("iconst_1") /*       */, BYTECODE_04_ICONST_1 /*       */, encode_only_bcode },
	{ NS("iconst_0") /*       */, BYTECODE_03_ICONST_0 /*       */, encode_only_bcode },
	{ NS("iastore") /*        */, BYTECODE_4F_IASTORE /*        */, encode_only_bcode },
	{ NS("iand") /*           */, BYTECODE_7E_IAND /*           */, encode_only_bcode },
	{ NS("iaload") /*         */, BYTECODE_2E_IALOAD /*         */, encode_only_bcode },
	{ NS("iadd") /*           */, BYTECODE_60_IADD /*           */, encode_only_bcode },
	{ NS("i2s") /*            */, BYTECODE_93_I2S /*            */, encode_only_bcode },
	{ NS("i2l") /*            */, BYTECODE_85_I2L /*            */, encode_only_bcode },
	{ NS("i2f") /*            */, BYTECODE_86_I2F /*            */, encode_only_bcode },
	{ NS("i2d") /*            */, BYTECODE_87_I2D /*            */, encode_only_bcode },
	{ NS("i2c") /*            */, BYTECODE_92_I2C /*            */, encode_only_bcode },
	{ NS("i2b") /*            */, BYTECODE_91_I2B /*            */, encode_only_bcode },
	{ NS("goto_w") /*         */, BYTECODE_C8_GOTO_W /*         */, encode_addr32 },
	{ NS("goto") /*           */, BYTECODE_A7_GOTO /*           */, encode_addr16 },
	{ NS("getstatic") /*      */, BYTECODE_B2_GETSTATIC /*      */, encode_const_pool16 },
	{ NS("getfield") /*       */, BYTECODE_B4_GETFIELD /*       */, encode_const_pool16 },
	{ NS("fsub") /*           */, BYTECODE_66_FSUB /*           */, encode_only_bcode },
	{ NS("fstore_3") /*       */, BYTECODE_46_FSTORE_3 /*       */, encode_only_bcode },
	{ NS("fstore_2") /*       */, BYTECODE_45_FSTORE_2 /*       */, encode_only_bcode },
	{ NS("fstore_1") /*       */, BYTECODE_44_FSTORE_1 /*       */, encode_only_bcode },
	{ NS("fstore_0") /*       */, BYTECODE_43_FSTORE_0 /*       */, encode_only_bcode },
	{ NS("fstore") /*         */, BYTECODE_38_FSTORE /*         */, encode_ut8 },
	{ NS("freturn") /*        */, BYTECODE_AE_FRETURN /*        */, encode_only_bcode },
	{ NS("frem") /*           */, BYTECODE_72_FREM /*           */, encode_only_bcode },
	{ NS("fneg") /*           */, BYTECODE_76_FNEG /*           */, encode_only_bcode },
	{ NS("fmul") /*           */, BYTECODE_6A_FMUL /*           */, encode_only_bcode },
	{ NS("fload_3") /*        */, BYTECODE_25_FLOAD_3 /*        */, encode_only_bcode },
	{ NS("fload_2") /*        */, BYTECODE_24_FLOAD_2 /*        */, encode_only_bcode },
	{ NS("fload_1") /*        */, BYTECODE_23_FLOAD_1 /*        */, encode_only_bcode },
	{ NS("fload_0") /*        */, BYTECODE_22_FLOAD_0 /*        */, encode_only_bcode },
	{ NS("fload") /*          */, BYTECODE_17_FLOAD /*          */, encode_ut8 },
	{ NS("fdiv") /*           */, BYTECODE_6E_FDIV /*           */, encode_only_bcode },
	{ NS("fconst_2") /*       */, BYTECODE_0D_FCONST_2 /*       */, encode_only_bcode },
	{ NS("fconst_1") /*       */, BYTECODE_0C_FCONST_1 /*       */, encode_only_bcode },
	{ NS("fconst_0") /*       */, BYTECODE_0B_FCONST_0 /*       */, encode_only_bcode },
	{ NS("fcmpl") /*          */, BYTECODE_95_FCMPL /*          */, encode_only_bcode },
	{ NS("fcmpg") /*          */, BYTECODE_96_FCMPG /*          */, encode_only_bcode },
	{ NS("fastore") /*        */, BYTECODE_51_FASTORE /*        */, encode_only_bcode },
	{ NS("faload") /*         */, BYTECODE_30_FALOAD /*         */, encode_only_bcode },
	{ NS("fadd") /*           */, BYTECODE_62_FADD /*           */, encode_only_bcode },
	{ NS("f2l") /*            */, BYTECODE_8C_F2L /*            */, encode_only_bcode },
	{ NS("f2i") /*            */, BYTECODE_8B_F2I /*            */, encode_only_bcode },
	{ NS("f2d") /*            */, BYTECODE_8D_F2D /*            */, encode_only_bcode },
	{ NS("dup_x2") /*         */, BYTECODE_5B_DUP_X2 /*         */, encode_only_bcode },
	{ NS("dup_x1") /*         */, BYTECODE_5A_DUP_X1 /*         */, encode_only_bcode },
	{ NS("dup2_x2") /*        */, BYTECODE_5E_DUP2_X2 /*        */, encode_only_bcode },
	{ NS("dup2_x1") /*        */, BYTECODE_5D_DUP2_X1 /*        */, encode_only_bcode },
	{ NS("dup2") /*           */, BYTECODE_5C_DUP2 /*           */, encode_only_bcode },
	{ NS("dup") /*            */, BYTECODE_59_DUP /*            */, encode_only_bcode },
	{ NS("dsub") /*           */, BYTECODE_67_DSUB /*           */, encode_only_bcode },
	{ NS("dstore_3") /*       */, BYTECODE_4A_DSTORE_3 /*       */, encode_only_bcode },
	{ NS("dstore_2") /*       */, BYTECODE_49_DSTORE_2 /*       */, encode_only_bcode },
	{ NS("dstore_1") /*       */, BYTECODE_48_DSTORE_1 /*       */, encode_only_bcode },
	{ NS("dstore_0") /*       */, BYTECODE_47_DSTORE_0 /*       */, encode_only_bcode },
	{ NS("dstore") /*         */, BYTECODE_39_DSTORE /*         */, encode_ut8 },
	{ NS("dreturn") /*        */, BYTECODE_AF_DRETURN /*        */, encode_only_bcode },
	{ NS("drem") /*           */, BYTECODE_73_DREM /*           */, encode_only_bcode },
	{ NS("dneg") /*           */, BYTECODE_77_DNEG /*           */, encode_only_bcode },
	{ NS("dmul") /*           */, BYTECODE_6B_DMUL /*           */, encode_only_bcode },
	{ NS("dload_3") /*        */, BYTECODE_29_DLOAD_3 /*        */, encode_only_bcode },
	{ NS("dload_2") /*        */, BYTECODE_28_DLOAD_2 /*        */, encode_only_bcode },
	{ NS("dload_1") /*        */, BYTECODE_27_DLOAD_1 /*        */, encode_only_bcode },
	{ NS("dload_0") /*        */, BYTECODE_26_DLOAD_0 /*        */, encode_only_bcode },
	{ NS("dload") /*          */, BYTECODE_18_DLOAD /*          */, encode_ut8 },
	{ NS("ddiv") /*           */, BYTECODE_6F_DDIV /*           */, encode_only_bcode },
	{ NS("dconst_1") /*       */, BYTECODE_0F_DCONST_1 /*       */, encode_only_bcode },
	{ NS("dconst_0") /*       */, BYTECODE_0E_DCONST_0 /*       */, encode_only_bcode },
	{ NS("dcmpl") /*          */, BYTECODE_97_DCMPL /*          */, encode_only_bcode },
	{ NS("dcmpg") /*          */, BYTECODE_98_DCMPG /*          */, encode_only_bcode },
	{ NS("dastore") /*        */, BYTECODE_52_DASTORE /*        */, encode_only_bcode },
	{ NS("daload") /*         */, BYTECODE_31_DALOAD /*         */, encode_only_bcode },
	{ NS("dadd") /*           */, BYTECODE_63_DADD /*           */, encode_only_bcode },
	{ NS("d2l") /*            */, BYTECODE_8F_D2L /*            */, encode_only_bcode },
	{ NS("d2i") /*            */, BYTECODE_8E_D2I /*            */, encode_only_bcode },
	{ NS("d2f") /*            */, BYTECODE_90_D2F /*            */, encode_only_bcode },
	{ NS("checkcast") /*      */, BYTECODE_C0_CHECKCAST /*      */, encode_const_pool16 },
	{ NS("castore") /*        */, BYTECODE_55_CASTORE /*        */, encode_only_bcode },
	{ NS("caload") /*         */, BYTECODE_34_CALOAD /*         */, encode_only_bcode },
	{ NS("breakpoint") /*     */, BYTECODE_CA_BREAKPOINT /*     */, encode_only_bcode },
	{ NS("bipush") /*         */, BYTECODE_10_BIPUSH /*         */, encode_st8 },
	{ NS("bastore") /*        */, BYTECODE_54_BASTORE /*        */, encode_only_bcode },
	{ NS("baload") /*         */, BYTECODE_33_BALOAD /*         */, encode_only_bcode },
	{ NS("athrow") /*         */, BYTECODE_BF_ATHROW /*         */, encode_only_bcode },
	{ NS("astore_3") /*       */, BYTECODE_4E_ASTORE_3 /*       */, encode_only_bcode },
	{ NS("astore_2") /*       */, BYTECODE_4D_ASTORE_2 /*       */, encode_only_bcode },
	{ NS("astore_1") /*       */, BYTECODE_4C_ASTORE_1 /*       */, encode_only_bcode },
	{ NS("astore_0") /*       */, BYTECODE_4B_ASTORE_0 /*       */, encode_only_bcode },
	{ NS("astore") /*         */, BYTECODE_3A_ASTORE /*         */, encode_ut8 },
	{ NS("arraylength") /*    */, BYTECODE_BE_ARRAYLENGTH /*    */, encode_only_bcode },
	{ NS("areturn") /*        */, BYTECODE_B0_ARETURN /*        */, encode_only_bcode },
	{ NS("anewarray") /*      */, BYTECODE_BD_ANEWARRAY /*      */, encode_const_pool16 },
	{ NS("aload_3") /*        */, BYTECODE_2D_ALOAD_3 /*        */, encode_only_bcode },
	{ NS("aload_2") /*        */, BYTECODE_2C_ALOAD_2 /*        */, encode_only_bcode },
	{ NS("aload_1") /*        */, BYTECODE_2B_ALOAD_1 /*        */, encode_only_bcode },
	{ NS("aload_0") /*        */, BYTECODE_2A_ALOAD_0 /*        */, encode_only_bcode },
	{ NS("aload") /*          */, BYTECODE_19_ALOAD /*          */, encode_ut8 },
	{ NS("aconst_null") /*    */, BYTECODE_01_ACONST_NULL /*    */, encode_only_bcode },
	{ NS("aastore") /*        */, BYTECODE_53_AASTORE /*        */, encode_only_bcode },
	{ NS("aaload") /*         */, BYTECODE_32_AALOAD /*         */, encode_only_bcode }
};
#undef NS

bool java_assembler(const char *input, st32 input_size, ut8 *output, st32 output_size, ut64 pc, st32 *written) {
	rz_return_val_if_fail(input && output && input_size > 0 && output_size > 0, false);

	for (ut32 i = 0; i < RZ_ARRAY_SIZE(instructions); ++i) {
		if (input_size < instructions[i].length) {
			continue;
		}
		if (!rz_str_ncasecmp(input, instructions[i].opcode, instructions[i].length)) {
			const char *p = rz_str_trim_head_ro(input + instructions[i].length);
			st32 used = p ? (p - input) : input_size;
			return instructions[i].encode(instructions[i].bytecode, p, input_size - used, output, output_size, pc, written);
		}
	}

	RZ_LOG_ERROR("[!] java_assembler: invalid assembly.\n");
	return false;
}
