Enables checking for casting validity. In case of invalid cast, it aborts instead of throwing ClassCastException.
Signed-off-by: Saeed Siam<[email protected]> --- diff --git a/arch/x86/insn-selector_32.brg b/arch/x86/insn-selector_32.brg index 47d3f3e..d2474df 100644 --- a/arch/x86/insn-selector_32.brg +++ b/arch/x86/insn-selector_32.brg @@ -1047,6 +1047,22 @@ stmt: STMT_MONITOR_EXIT(reg) method_args_cleanup(s, 1); } +stmt: STMT_CHECKCAST(reg) +{ + struct statement *stmt; + struct var_info *ref; + + ref = state->left->reg1; + + stmt = to_stmt(tree); + + bb_add_insn(s, imm_insn(INSN_PUSH_IMM, (unsigned long) stmt->checkcast_class)); + bb_add_insn(s, reg_insn(INSN_PUSH_REG, ref)); + bb_add_insn(s, rel_insn(INSN_CALL_REL, (unsigned long)check_cast)); + + method_args_cleanup(s, 2); +} + %% static void __binop_reg_local(struct _MBState *state, struct basic_block *bb, diff --git a/include/jit/statement.h b/include/jit/statement.h index 218e228..f87abc0 100644 --- a/include/jit/statement.h +++ b/include/jit/statement.h @@ -18,6 +18,7 @@ enum statement_type { STMT_ATHROW, STMT_MONITOR_ENTER, STMT_MONITOR_EXIT, + STMT_CHECKCAST, STMT_LAST, /* Not a real type. Keep this last. */ }; @@ -41,6 +42,10 @@ struct statement { struct /* STMT_RETURN */ { struct tree_node *return_value; }; + struct /* STMT_CHECKCAST */ { + struct tree_node *checkcast_ref; + struct object *checkcast_class; + }; /* STMT_EXPRESSION, STMT_NULL_CHECK, STMT_ARRAY_CHECK */ struct tree_node *expression; }; diff --git a/include/vm/class.h b/include/vm/class.h index 3b4004b..4ca0274 100644 --- a/include/vm/class.h +++ b/include/vm/class.h @@ -6,5 +6,6 @@ unsigned long is_object_instance_of(struct object *obj, struct object *type); void check_null(struct object *obj); void check_array(struct object *obj, unsigned int index); +void check_cast(struct object *obj, struct object *type); #endif /* __CLASS_H */ diff --git a/jit/object-bc.c b/jit/object-bc.c index b1d5acf..77fbf46 100644 --- a/jit/object-bc.c +++ b/jit/object-bc.c @@ -468,7 +468,29 @@ int convert_instanceof(struct parse_context *ctx) int convert_checkcast(struct parse_context *ctx) { - /* TODO */ + struct expression *object_ref; + struct object *class; + struct statement *checkcast_stmt; + unsigned long type_idx; + + object_ref = stack_pop(ctx->bb->mimic_stack); + + type_idx = bytecode_read_u16(ctx->buffer); + class = resolveClass(ctx->cu->method->class, type_idx, FALSE); + if (!class) + return -ENOMEM; + + checkcast_stmt = alloc_statement(STMT_CHECKCAST); + if (!checkcast_stmt) + return -ENOMEM; + + checkcast_stmt->checkcast_class = class; + checkcast_stmt->checkcast_ref = &object_ref->node; + + expr_get(object_ref); + bb_add_stmt(ctx->bb, checkcast_stmt); + stack_push(ctx->bb->mimic_stack, object_ref); + return 0; } diff --git a/jit/statement.c b/jit/statement.c index b75ade7..0c8ecf4 100644 --- a/jit/statement.c +++ b/jit/statement.c @@ -54,6 +54,10 @@ void free_statement(struct statement *stmt) if (stmt->expression) expr_put(to_expr(stmt->expression)); break; + case STMT_CHECKCAST: + if (stmt->checkcast_ref) + expr_put(to_expr(stmt->checkcast_ref)); + break; case STMT_LAST: assert(!"STMT_LAST is not a real type. Don't use it!"); break; diff --git a/jit/tree-printer.c b/jit/tree-printer.c index 9f87bf1..3e4c948 100644 --- a/jit/tree-printer.c +++ b/jit/tree-printer.c @@ -240,6 +240,25 @@ static int print_monitor_exit_stmt(int lvl, struct string *str, return print_expr_stmt(lvl, str, "MONITOR_EXIT", stmt); } +static int print_checkcast_stmt(int lvl, struct string *str, + struct statement *stmt) +{ + int err; + + err = append_formatted(lvl, str, "CHECKCAST:\n"); + if (err) + goto out; + + err = append_simple_attr(lvl + 1, str, "checkcast_type", "%p", stmt->checkcast_class); + if (err) + goto out; + + err = append_tree_attr(lvl + 1, str, "checkcast_ref", stmt->checkcast_ref); + + out: + return err; +} + typedef int (*print_stmt_fn) (int, struct string * str, struct statement *); static print_stmt_fn stmt_printers[] = { @@ -253,6 +272,7 @@ static print_stmt_fn stmt_printers[] = { [STMT_ARRAY_CHECK] = print_array_check_stmt, [STMT_MONITOR_ENTER] = print_monitor_enter_stmt, [STMT_MONITOR_EXIT] = print_monitor_exit_stmt, + [STMT_CHECKCAST] = print_checkcast_stmt, }; static int print_stmt(int lvl, struct tree_node *root, struct string *str) diff --git a/regression/jamvm/ObjectCreationAndManipulationTest.java b/regression/jamvm/ObjectCreationAndManipulationTest.java index 7a1b32e..9e4c922 100644 --- a/regression/jamvm/ObjectCreationAndManipulationTest.java +++ b/regression/jamvm/ObjectCreationAndManipulationTest.java @@ -105,6 +105,25 @@ public class ObjectCreationAndManipulationTest extends TestCase { array[0][0] = 0; } + public static void testCheckCast() { + Object object = new InstanceFields(); + + InstanceFields instanceFields = (InstanceFields) null; + assertNull(instanceFields); + + instanceFields = (InstanceFields) object; + assertNotNull(instanceFields); + + //Following test will fail. + + /* + ClassFields classField = new ClassFields(); + object = classField; + instanceFields = (InstanceFields) object; + */ + + } + public static void main(String[] args) { testNewObject(); testObjectInitialization(); @@ -121,6 +140,7 @@ public class ObjectCreationAndManipulationTest extends TestCase { testShortArrayLoadAndStore(); testObjectArrayLoadAndStore(); testMultiDimensionalArrayLoadAndStore(); + testCheckCast(); Runtime.getRuntime().halt(retval); } diff --git a/test/arch-x86/insn-selector-test.c b/test/arch-x86/insn-selector-test.c index e800c5f..c568209 100644 --- a/test/arch-x86/insn-selector-test.c +++ b/test/arch-x86/insn-selector-test.c @@ -1619,3 +1619,45 @@ void test_select_array_check_stmt(void) free_compilation_unit(cu); } + +void test_select_checkcast_stmt() { + struct compilation_unit *cu; + struct basic_block *bb; + struct statement *stmt; + struct expression *expr; + struct insn *insn; + struct object *class; + enum machine_reg dreg; + + class = new_class(); + expr = value_expr(J_REFERENCE, (unsigned long)class); + + stmt = alloc_statement(STMT_CHECKCAST); + stmt->checkcast_class = class; + stmt->checkcast_ref = &expr->node; + + cu = alloc_compilation_unit(&method); + bb = get_basic_block(cu, 0, 1); + bb_add_stmt(bb, stmt); + + select_instructions(bb->b_parent); + + insn = list_first_entry(&bb->insn_list, struct insn, insn_list_node); + dreg = mach_reg(&insn->dest.reg); + assert_imm_reg_insn(INSN_MOV_IMM_REG, (unsigned long) class, dreg, insn); + + insn = list_next_entry(&insn->insn_list_node, struct insn, insn_list_node); + assert_imm_insn(INSN_PUSH_IMM, (unsigned long) class, insn); + + insn = list_next_entry(&insn->insn_list_node, struct insn, insn_list_node); + assert_reg_insn(INSN_PUSH_REG, dreg, insn); + + insn = list_next_entry(&insn->insn_list_node, struct insn, insn_list_node); + assert_rel_insn(INSN_CALL_REL, (unsigned long) check_cast, insn); + + insn = list_next_entry(&insn->insn_list_node, struct insn, insn_list_node); + assert_imm_reg_insn(INSN_ADD_IMM_REG, 8, REG_ESP, insn); + + free(class); + free_compilation_unit(cu); +} diff --git a/test/jit/bc-test-utils.c b/test/jit/bc-test-utils.c index 99102d8..088b408 100644 --- a/test/jit/bc-test-utils.c +++ b/test/jit/bc-test-utils.c @@ -235,6 +235,13 @@ void assert_monitor_exit_stmt(struct expression *expected, assert_value_expr(J_REFERENCE, expected->value, actual->expression); } +void assert_checkcast_stmt(struct expression *expected, + struct statement *actual) +{ + assert_int_equals(STMT_CHECKCAST, stmt_type(actual)); + assert_value_expr(J_REFERENCE, expected->value, actual->expression); +} + void convert_ir_const(struct compilation_unit *cu, ConstantPoolEntry *cp_infos, size_t nr_cp_infos, u1 *cp_types) diff --git a/test/jit/bc-test-utils.h b/test/jit/bc-test-utils.h index b43b4f3..8ec8535 100644 --- a/test/jit/bc-test-utils.h +++ b/test/jit/bc-test-utils.h @@ -44,6 +44,7 @@ void assert_arraycheck_stmt(enum vm_type, struct expression *, struct expression *, struct statement *); void assert_monitor_enter_stmt(struct expression *, struct statement *); void assert_monitor_exit_stmt(struct expression *, struct statement *); +void assert_checkcast_stmt(struct expression *, struct statement *); void convert_ir_const(struct compilation_unit *, ConstantPoolEntry *, size_t, u1 *); diff --git a/test/jit/object-bc-test.c b/test/jit/object-bc-test.c index bc3fb0a..aaf1016 100644 --- a/test/jit/object-bc-test.c +++ b/test/jit/object-bc-test.c @@ -644,3 +644,30 @@ void test_convert_monitor_exit(void) __free_simple_bb(bb); free(class); } + +void test_convert_checkcast(void) +{ + unsigned char code[] = {OPC_CHECKCAST, 0x00, 0x00}; + struct methodblock method = { + .jit_code = code, + .code_size = ARRAY_SIZE(code), + }; + + struct expression *ref; + struct statement *stmt; + struct basic_block *bb; + struct object *class = new_class(); + + bb = __alloc_simple_bb(&method); + + ref = value_expr(J_REFERENCE, (unsigned long)class); + stack_push(bb->mimic_stack, ref); + + convert_ir_const_single(bb->b_parent, class); + stmt = stmt_entry(bb->stmt_list.next); + assert_checkcast_stmt(ref, stmt); + + expr_put(ref); + __free_simple_bb(bb); + free(class); +} diff --git a/test/jit/tree-printer-test.c b/test/jit/tree-printer-test.c index 6d63f18..2793aad 100644 --- a/test/jit/tree-printer-test.c +++ b/test/jit/tree-printer-test.c @@ -166,6 +166,22 @@ void test_should_print_monitorexit_statement(void) assert_print_stmt("MONITOR_EXIT:\n expression: [local int 0]\n", stmt); } +void test_should_print_checkcast_statement(void) +{ + struct expression *expr; + struct statement *stmt; + + expr = local_expr(J_INT, 0); + + stmt = alloc_statement(STMT_CHECKCAST); + stmt->checkcast_ref = &expr->node; + stmt->checkcast_class = (void *)0xcafebabe; + + assert_print_stmt("CHECKCAST:\n" + " checkcast_type: [0xcafebabe]\n" + " checkcast_ref: [local int 0]\n", stmt); +} + static void assert_print_expr(const char *expected, struct expression *expr) { assert_tree_print(expected, &expr->node); diff --git a/vm/class.c b/vm/class.c index 0c040a9..c0e1e61 100644 --- a/vm/class.c +++ b/vm/class.c @@ -51,3 +51,12 @@ void check_array(struct object *obj, unsigned int index) if (index >= ARRAY_LEN(obj)) abort(); } + +void check_cast(struct object *obj, struct object *type) +{ + if (!obj) + return; + + if (!isInstanceOf(type, obj->class)) + abort(); +} ------------------------------------------------------------------------------ _______________________________________________ Jatovm-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/jatovm-devel
