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

Reply via email to