diff options
author | Tom Tromey <tromey@gcc.gnu.org> | 2004-11-25 03:47:08 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2004-11-25 03:47:08 +0000 |
commit | 367390404d26b7bfc400d77893579e83e2a19fb9 (patch) | |
tree | 477abdf83653e20b0e74447d6ca47eb67b0511b8 /gcc/java | |
parent | ec0641f612862498e829fdaf040a201c0ba68762 (diff) | |
download | gcc-367390404d26b7bfc400d77893579e83e2a19fb9.zip gcc-367390404d26b7bfc400d77893579e83e2a19fb9.tar.gz gcc-367390404d26b7bfc400d77893579e83e2a19fb9.tar.bz2 |
* Merged gcj-abi-2-dev-branch to trunk.
(Actual changes too large to list in the commit message;
see ChangeLog.)
From-SVN: r91270
Diffstat (limited to 'gcc/java')
-rw-r--r-- | gcc/java/ChangeLog | 583 | ||||
-rw-r--r-- | gcc/java/Make-lang.in | 5 | ||||
-rw-r--r-- | gcc/java/check-init.c | 60 | ||||
-rw-r--r-- | gcc/java/class.c | 349 | ||||
-rw-r--r-- | gcc/java/decl.c | 76 | ||||
-rw-r--r-- | gcc/java/expr.c | 539 | ||||
-rw-r--r-- | gcc/java/gcj.texi | 23 | ||||
-rw-r--r-- | gcc/java/gjavah.c | 5 | ||||
-rw-r--r-- | gcc/java/java-except.h | 2 | ||||
-rw-r--r-- | gcc/java/java-tree.h | 75 | ||||
-rw-r--r-- | gcc/java/jcf-parse.c | 142 | ||||
-rw-r--r-- | gcc/java/jvspec.c | 2 | ||||
-rw-r--r-- | gcc/java/lang.c | 20 | ||||
-rw-r--r-- | gcc/java/lang.opt | 4 | ||||
-rw-r--r-- | gcc/java/parse.y | 22 | ||||
-rw-r--r-- | gcc/java/typeck.c | 2 | ||||
-rw-r--r-- | gcc/java/verify-glue.c | 514 | ||||
-rw-r--r-- | gcc/java/verify-impl.c | 3418 | ||||
-rw-r--r-- | gcc/java/verify.c | 106 | ||||
-rw-r--r-- | gcc/java/verify.h | 160 |
20 files changed, 5794 insertions, 313 deletions
diff --git a/gcc/java/ChangeLog b/gcc/java/ChangeLog index 272e8c5..fc28a08 100644 --- a/gcc/java/ChangeLog +++ b/gcc/java/ChangeLog @@ -1,3 +1,586 @@ +2004-11-24 Tom Tromey <tromey@redhat.com> + + * Merged gcj-abi-2-dev-branch to trunk. + +2004-11-24 Andrew Haley <aph@redhat.com> + + * jcf-parse.c (parse_class_file): Set file_start_location. + +2004-11-10 Tom Tromey <tromey@redhat.com> + + * class.c (make_field_value): Don't call build_static_field_ref. + (build_static_field_ref): Don't emit direct references when using + indirect dispatch. + + * gcj.texi (Invoking gij): Document -verbose. Put -verbose and + -verbose:class into man page synopsis. + +2004-11-09 Tom Tromey <tromey@redhat.com> + + * expr.c (build_java_arraystore_check): Still generate check if + element type is itself an array. + +2004-11-08 Tom Tromey <tromey@redhat.com> + + * java-tree.h (soft_check_assignment_node): Removed. + (enum java_tree_index): Removed JTI_SOFT_CHECK_ASSIGNMENT_NODE. + * decl.c (java_init_decl_processing): Don't initialize + soft_check_assignment_node. + +2004-11-05 Tom Tromey <tromey@redhat.com> + + * class.c (layout_class_methods): Don't add Miranda methods when + using indirect dispatch. + +2004-11-05 Bryce McKinlay <mckinlay@redhat.com> + + * class.c (make_class_data): Call emit_assertion_table to set the + 'assertion_table' field. + (build_signature_for_libgcj): Move here from expr.c. + (add_assertion_table_entry): New function. Callback for assertion + hashtable traversal. + (emit_assertion_table): New. Take class argument, and generate + assertion table DECL based on the TYPE_ASSERTIONS hashtable. + * decl.c (init_decl_processing): Define assertion_entry_type record. + Push 'assertion_table' class field instead of 'verify'. + * expr.c (type_assertion_eq): Compare 'assertion_code' field. + (type_assertion_hash): Include 'assertion_code' in hash. + (add_type_assertion): Rewritten. Take class and assertion_code + arguments. Add assertions to the TYPE_ASSERTIONS hashtable. + (can_widen_reference_to): Use new add_type_assertion() arguments. + * java-tree.h (java_tree_index): Add JTI_ASSERTION_ENTRY_TYPE, + JTI_ASSERTION_TABLE_TYPE. Remove JTI_VERIFY_IDENTIFIER_NODE. + (verify_identifier_node): Removed. + (assertion_entry_type, assertion_table_type): New. + (ASSERTION_TYPES_COMPATIBLE, ASSERTION_IS_INSTANTIABLE): New. Type + assertion code definitions. + (struct type_assertion): Add assertion_code. Rename 'source_type' and + 'target_type' to 'op1' and 'op2'. + (add_type_assertion): Declare. + (lang_printable_name_wls): Remove unused definition. + * verify-glue.c: (vfy_is_assignable_from): New. Call add_type_assertion + to emit runtime assertion. + (vfy_note_stack_type): Clean up non-C90 declarations. + (vfy_note_local_type): Likewise. + * verify.h (vfy_is_assignable_from): Declare. + * verify-impl.c (is_assignable_from_slow): Remove unused function. + (ref_compatible): Rename arguments. Call vfy_is_assignable_from() + instead of is_assignable_from_slow(). + (types_compatible): Reinstate ref_compatible() call. + +2004-11-04 Tom Tromey <tromey@redhat.com> + + * class.c (build_static_field_ref): Reverted previous patch. + + * class.c (build_static_field_ref): Don't emit direct references + when using indirect dispatch. + +2004-11-03 Tom Tromey <tromey@redhat.com> + + * expr.c (expand_java_arrayload): Set lhs_type_node. + (expand_java_arraystore): Set rhs_type_node. + +2004-11-02 Tom Tromey <tromey@redhat.com> + + * jcf-parse.c (compute_class_name): Use filename length from zip + directory, not strlen. + + * expr.c (expand_invoke): Mark new interface methods as abstract. + +2004-11-01 Tom Tromey <tromey@redhat.com> + + * verify-impl.c (push_jump): Removed check for uninitialized + objects. + (push_exception_jump): Likewise. + (handle_ret_insn): Likewise. + (handle_jsr_insn): Likewise. + (state_check_no_uninitialized_objects): Removed. + + * decl.c (check_local_unnamed_variable): Recognize + promoted-to-int parameters when using the new verifier. + * expr.c (expand_java_arraystore): Explicitly request array type + when using new verifier. + (expand_java_arrayload): Likewise. + (invoke_build_dtable): Don't pass object_type_node as + expression argument to build_java_indirect_ref. + (build_java_check_indexed_type): Do nothing. + (build_java_arraystore_check): Handle case where array doesn't + have array type. + (build_java_array_length_access): Likewise. + (expand_invoke): Handle case where interface overrides a method + from Object. + (pop_type_0): Always succeed for reference types. + (process_jvm_instruction): Don't pop a value in a dead + exception handler. + (pop_arguments): Convert arguments to correct types. + +2004-10-29 Andrew Haley <aph@redhat.com> + + * jcf-parse.c (give_name_to_class): Remove line that was + incorrectly merged. + +2004-10-29 Andrew Haley <aph@redhat.com> + + * jcf-parse.c (set_source_filename): Add code to build new sfname. + +2004-10-20 Andrew Haley <aph@redhat.com> + + * decl.c (end_java_method): Don't expand if flag_syntax_only. + +2004-10-26 Tom Tromey <tromey@redhat.com> + + * verify.h (vfy_notify_verified): Removed. + * verify-glue.c (vfy_notify_verified): Removed. + +2004-10-26 Tom Tromey <tromey@redhat.com> + + * verify-impl.c (debug_print_state): Declare `i' before code. + (merge_types): Modify `t' when it is null_type. + +2004-10-26 Tom Tromey <tromey@redhat.com> + + * verify-impl.c (type_print): Renamed from print. Now static and + takes an argument. + (debug_print_state): Use type_print. + +2004-10-25 Tom Tromey <tromey@redhat.com> + + * expr.c (build_invokeinterface): Compute correct offset for + index into interface methods. + +2004-10-20 Tom Tromey <tromey@redhat.com> + + * java-tree.h (verify_jvm_instructions_new): Declare. + + * jvspec.c (jvgenmain_spec): Remove -fnew-verifier from cc1 + command line. + + * verify-impl.c (verify_instructions): Correctly handle wide + types on the stack. + * verify-glue.c (vfy_get_class_name): Use DECL_NAME. + (vfy_get_component_type): Strip pointer types. + (vfy_find_class): Use get_type_from_signature. Strip pointer + types. + Include java-except.h. + +2004-10-20 Bryce McKinlay <mckinlay@redhat.com> + + * verify-impl.c (type_array_elementpop_raw, vfy_pop_type_t, + vfy_push_type_t, set_variable, add_new_state, merge_into, + handle_jsr_insn, branch_prepass, check_class_constant, + check_wide_constant, get_one_type, compute_static_types, + verify_instructions_0): Clean up C99 declarations after statements. + +2004-10-20 Tom Tromey <tromey@redhat.com> + + * verify-impl.c (merge_refs): Compare reference against iterator, + not ref2. + + * verify-glue.c (vfy_tag): Mask off resolved flag. + +2004-10-19 Tom Tromey <tromey@redhat.com> + + * verify-impl.c (verify_instructions): Call vfy_note_local_type. + (init_state_with_stack): Initialize `this_type' in state. + (verify_method): Use debug_print. + * verify-glue.c (vfy_is_primitive): Removed debugging print. + (vfy_note_stack_depth): Reverted last patch. + (vfy_note_stack_type): Note pointer to Object, not Object. + (vfy_note_local_type): Likewise. + + * verify.h (vfy_note_instruction_seen): Declare. + * verify-glue.c (verify_jvm_instructions_new): Set + BCODE_EXCEPTION_TARGET on target instruction. + (vfy_note_instruction_seen): New function. + * verify-impl.c (FLAG_INSN_SEEN): New define. + (verify_instructions_0): Set flag on instruction. Save state for + PC=0 later. + (verify_instructions): Call vfy_note_instruction_seen. + + * verify-glue.c (vfy_note_stack_depth): Fix off-by-one error. + (verify_jvm_instructions_new): Call method_init_exceptions, + add_handler, and handle_nested_ranges. + * verify-impl.c (verify_method): Return 1 on success. + (verify_instructions_0): Save the state at PC=0. + + * verify-impl.c (init_type_from_class): Set is_resolved and + ref_next on new ref_intersection. + (init_type_from_string): Likewise. + +2004-10-15 Bryce McKinlay <mckinlay@redhat.com> + + * expr.c (expand_bytecode): Use verify_jvm_instructions_new + if flag_new_verifier is set. + * java-tree.h (flag_new_verifier): Declare. + * lang.opt (fnew-verifier): New option. + * verify-impl.c: Work around namespace pollution by undef'ing + 'current_class'. + (struct verifier_context): Make 'bytecode' const. + (verify_fail_pc): Pass -1 PC argument to vfy_fail. + (types_compatible): For the BC-ABI, always consider reference types + compatible. + (check_class_constant): Use vfr->current_class. + (check_constant): Likewise. + (check_wide_constant): Likewise. + (check_field_constant): Check for 'L' at start of type name. + (get_one_type): Return pointer instead of type. Set type result in + caller via passed type pointer. + (compute_argument_types): Update to use new get_one_type arguments. + (compute_return_type): Likewise. + (make_verifier_context): New. Allocate and initialize 'vfr'. + (free_verifier_context): New. Free 'vfr' and its contents. + (verify_method): Remove ATTRIBUTE_UNUSED. Call make_verifier_context + and free_verifier_context. + +2004-10-15 Tom Tromey <tromey@redhat.com> + + * verify-glue.c (vfy_note_local_type): Mark argument as unused. + * verify.h (vfy_fail): Fixed formatting. + + * verify-impl.c (vfr): Fixed comment formatting. + (collapse_type): New function. + (verify_instructions): Notify compiler about type map. + * verify.h (vfy_note_stack_depth): Updated. + (vfy_note_stack_type): Likewise. + (vfy_note_local_type): Likewise. + (vfy_unsuitable_type, vfy_return_address_type, vfy_null_type): + Declare. + * verify-glue.c (vfy_note_stack_depth): Correctly size type + state. Added `method' argument. + (vfy_note_stack_type): Renamed from vfy_note_type. Added `method' + argument. + (vfy_note_local_type): New function. + (vfy_unsuitable_type): Likewise. + (vfy_return_address_type): Likewise. + (vfy_null_type): Likewise. + + * verify.h (VFY_IN_GCC): Removed. + (VFY_WANT_TYPEMAP): Removed. + * verify-impl.c (verify_instructions_0): Removed useless "\". + (struct state) <next>: Uncomment. + +2004-10-13 Bryce McKinlay <mckinlay@redhat.com> + + * verify-impl.c: Formatting fixes. Reformat C++-style comments to + C-style. + +2004-10-06 Bryce McKinlay <mckinlay@redhat.com> + + * Make-lang.in (verify.o): Re-enabled this target. + * verify-glue.c (vfy_get_interface_count): Add ATTRIBUTE_UNUSED. + (vfy_get_interface): Likewise. + (verify_jvm_instructions_new): Renamed from verify_jvm_instructions. + * verify.h (verify_jvm_instructions_new): Declare. + * verify-impl.c (free_state): Temporarily comment out unused + function. + +2004-10-06 Tom Tromey <tromey@redhat.com> + + * java-tree.h (JV_STATE_READ): New enum value. + +2004-10-06 Bryce McKinlay <mckinlay@redhat.com> + + * verify.h: New file. + +2004-10-05 Bryce McKinlay <mckinlay@redhat.com> + + * verify-impl.c, verify-glue.c, verify.h: New files. + * Make-lang.in: Add rules for verify-impl.o and verify-glue.o. + +2004-09-24 Andrew Haley <aph@redhat.com> + + * decl.c (check_local_unnamed_variable): Always use the PARM_DECL + for a slot if it's of pointer type. + +2004-09-14 Tom Tromey <tromey@redhat.com> + + * class.c (make_class_data): Correctly initialize "state" field. + Initialize "engine" field. + * decl.c (java_init_decl_processing): Add "engine" field. + +2004-09-10 Andrew Haley <aph@redhat.com> + + PR java/12760 + * expr.c (build_invokeinterface): Use fast method for interface + dispatch. + * java-tree.h (enum java_tree_index): Add JTI_ITABLE_TYPE, + JTI_ITABLE_PTR_TYPE. + (struct lang_type): Add itable_methods, itable_decl, itable_syms_decl. + (emit_symbol_table): Add new arg, element_size. + * decl.c (java_init_decl_processing): Initialize Class.itable. + * class.c (GEN_TABLE): New macro. + (gen_indirect_dispatch_tables): Use it. Add itable. + (make_class_data): Add new arg for emit_symbol_table(). + Emit itable. + (add_miranda_methods): Make sure search_class has been parsed. + (emit_symbol_table): Add new arg, element_size. + +2004-09-06 Andrew Haley <aph@redhat.com> + + * verify.c (merge_types): Return Object for all merges of + interfaces. + * expr.c (add_type_assertion): Don't generate assertions when + source type is array of Object. + +2004-09-03 Andrew Haley <aph@redhat.com> + + * class.c (finish_class): Nullify TYPE_VERIFY_METHOD. + + * lang.c (java_post_options): Force flag_verify_invocations if + we're not using indirect dispatch. + + * expr.c (pop_type_0): Move test for interfaces before call to + can_widen_reference_to(). + (build_signature_for_libgcj): Remove generation of canonical array + type. + (add_type_assertion): Canonicalize both arrays. + Don't assert that type X can be assigned to Object. + Don't assert that type X an be assigned to type X. + Don't assert that Object can be assigned to type X. + (can_widen_reference_to): Warn whenever we generate an assertion. + (process_jvm_instruction): Use throwable_type_node for the type of + an exception class. + +2004-09-01 Andrew Haley <aph@redhat.com> + + * decl.c (java_init_decl_processing): Change + verify_identifier_node to "__verify". + * expr.c (add_type_assertion): Use verify_identifier_node for name. + * java-tree.h (verify_identifier_node): Change to "__verify". + + * expr.c (build_signature_for_libgcj): New function. + (add_type_assertion): Use it to construct signatures for + source_type and target_type. + +2004-08-27 Andrew Haley <aph@redhat.com> + + * java-tree.h (enum java_tree_index): Add JTI_VERIFY_IDENTIFIER_NODE. + (verify_identifier_node): New. + (TYPE_VERIFY_METHOD): New. + (struct type_assertion): New type. + * expr.c (type_assertion_eq): New function. + (type_assertion_hash): New function. + (add_type_assertion): New function. + (can_widen_reference_to): Call add_type_assertion(). + * decl.c (java_init_decl_processing): Add verify_identifier_node. + * class.c (make_class_data): Initialize TYPE_VERIFY_METHOD (type). + (finish_class): Output TYPE_VERIFY_METHOD (type). + + * decl.c (end_java_method): Nullify unused fields. + +2004-08-17 Andrew Haley <aph@redhat.com> + + * verify.c (defer_merging): Quieten. + * jcf-parse.c (load_class): Only try to open a class file if it's + java.lang.Object or if it's part of the current compilation. + Check that the class we just tried to load is the class we just + loaded. Quieten. + (java_parse_file): Set flag_verify_invocations off if we're + compiling from .class. + (parse_zip_file_entries): Abort if we try to read a dummy class. + * expr.c (can_widen_reference_to): Quieten. + (build_invokevirtual): Abort if we try to invokevirtual an + interface. + (expand_invoke): Don't build a non-interface call to an interface. + (build_instanceof): Don't do premature optimization if + flag_verify_invocations is not set. + * class.c (set_super_info): Disable code that inherits TYPE_DUMMY + from superclass. + (build_static_field_ref): Add correct type conversion for + field_address. + (add_miranda_methods): Disable generation of Miranda methods for + dummy classes. + (layout_class_method): Don't complain about non-static method + overrides static method with dummy classes. + +2004-08-13 Tom Tromey <tromey@redhat.com> + + * class.c (build_static_field_ref): Re-enable atable lookups for + static fields. + + * parse.y (strip_out_static_field_access_decl): Indentation fix. + +2004-08-11 Tom Tromey <tromey@redhat.com> + + * gcj.texi (libgcj Runtime Properties): Document new properties. + +2004-08-06 Andrew Haley <aph@redhat.com> + + * jcf-parse.c (load_class): Check that we really have loaded the + class we're looking for. + +2004-07-19 Andrew Haley <aph@redhat.com> + + * verify.c (verify_jvm_instructions): Comment change only. + + * typeck.c (build_java_array_type): Add size field to array name. + + * java-tree.h (LOCAL_SLOT_P): New. + (update_aliases): Add PC argument. + (pushdecl_function_level): New function. + + * java-gimplify.c (java_gimplify_expr): Handle VAR_DECL, + MODIFY_EXPR, and SAVE_EXPR. + (java_gimplify_modify_expr): New function. + + * expr.c (push_type_0): Call find_stack_slot() to create temporary. + (expand_iinc): Pass PC to update_aliases(). + (STORE_INTERNAL): Likewise. + (process_jvm_instruction): Likewise. + + * decl.c (base_decl_map): New variable. + (uniq): New variable. + (update_aliases): Rewrite with more thorough checking. + (debug_variable_p): New function. + (push_jvm_slot): Don't initialize local variable. Don't pushdecl. + (check_local_named_variable): Delete whole function. + (initialize_local_variable): New function. + (check_local_unnamed_variable): Add checks and comments. + (find_local_variable): Rewrite. + (java_replace_reference): New function. + (function_binding_level): New variable. + (pushdecl_function_level): New function. + (maybe_pushlevels): Set DECL_LOCAL_END_PC. + (maybe_pushlevels): Call pushdecl() on each of the new decls. + (start_java_method): Reset uniq. Create base_decl_map. Set + function_binding_level. + (end_java_method): Null unused fields to save memory. + +2004-06-29 Andrew Haley <aph@redhat.com> + + * except.c (expand_start_java_handler): Push a new binding level. + Don't build a TRY_CATCH_EXPR now, we'll do it later. Call + register_exception_range() to register where we'll do it. + (expand_end_java_handler): Remove old bogus code. Replace with + new logic that simply builds TRY_CATCH_EXPRs and inserts them at + the top of the expression we're curently building. + (maybe_end_try): Delete. + * decl.c (binding_level.exception_range): New field. + (clear_binding_level): Add field exception_range. Reformat. + (poplevel): Call expand_end_java_handler(). + (poplevel): Call java_add_stmt only if functionbody is false. + (maybe_poplevels): Don't call maybe_end_try() from here. + (end_java_method): Clear no longer used trees in function decl. + (register_exception_range): New function. + * java-tree.h (register_exception_range, struct eh_range): Declare. + +2004-06-22 Andrew Haley <aph@redhat.com> + + * class.c (gen_indirect_dispatch_tables): Set the DECL_OWNER of + the otable. + * check-init.c (get_variable_decl): Teach check-init about + FIELD_DECLs addressed via the otable. + * jcf-parse.c (load_class): Check CLASS_LOADED_P, not + CLASS_PARSED_P. + +2004-05-28 Andrew Haley <aph@redhat.com> + + * jcf-parse.c (load_class): Don't try to read a class that we've + already read. + + * expr.c (build_invokeinterface): Use the old-fashioned way of + doing indirect dispatch: look up interfaces by name. + * java-tree.h (enum java_tree_index): Add + JTI_SOFT_LOOKUPINTERFACEMETHODBYNAME_NODE + * decl.c (java_init_decl_processing): Add + soft_lookupinterfacemethodbyname_node. + + * gjavah.c (print_method_info): Final methods have vtable entries, + so gjavah needs to output them. + * class.c (layout_class_method): Generate vtable entries for final + methods. + * parse.y (invocation_mode): Use INVOKE_VIRTUAL for indirect + dispatch, even if a method is final. + +2004-05-25 Andrew Haley <aph@redhat.com> + + * class.c (build_symbol_entry): Convert the names of constructors + to init_identifier_node when generating an entry for the indirect + dispatch table. + + * expr.c (build_known_method_ref): Generate indirect calls for + all methods marked DECL_EXTERNAL or TREE_PUBLIC. + +2004-05-24 Andrew Haley <aph@redhat.com> + + * expr.c (build_known_method_ref): Make sure ARRAY_REF access to + atable element is of the right type. + + * class.c (build_static_field_ref): Cast pointer to correct type + for field. + +2004-04-20 Bryce McKinlay <mckinlay@redhat.com> + + * Merged with HEAD as of 20040514. Diff against + gcj-abi-2-merge-20040514. + +2004-04-16 Andrew Haley <aph@redhat.com> + + * verify.c (check_pending_block): Disable subroutine checks. + (defer_merging): New function. + (merge_types): If types are dummy, use defer_merging to combine them. + (verify_jvm_instructions): If invocation is invokeinterface and + target is dummy, assume target really is an interface. + + * parse.y (patch_invoke): Break out call to java_create_object. + + * lang.c (flag_verify_invocations): New. + + * jcf-parse.c (load_class): If we've already failed to load a + class, don't try again. + (load_class): If we can't find a .class file, don't fail, but emit + a warning. + (parse_class_file): Don't act on dummy methods. + + * java-tree.h (flag_verify_invocations): New. + (TYPE_DUMMY): New. + (lang_type.dummy_class): New field. + (java_create_object): New function. + (METHOD_DUMMY): New. + + * expr.c (build_field_ref): Widen field offset. + (pop_type_0): If the type in stack_type_map is a TREE_LIST, check + that each of its elements is compatible with the one we're + popping. + (pop_type_0): Issue a warning to say that we need to generate a + runtime check. + (java_create_object): New function. + (build_field_ref): Only generate hard refs if we're not using + indirect dispatch. + (expand_java_field_op): If we're using !verify_invocations and we + see a missing field, generate a decl for it. + + (expand_invoke): If a class doesn't have the method we seek and + we're using !flag_verify_invocations, generate a decl for the + method now. + + (build_known_method_ref): Always use indirect dispatch via the + atable for static methods. + + (expand_java_NEW): Break out object creation into new function, + java_create_object. + + (can_widen_reference_to): Issue a warning to say that we need to + generate a runtime check. + + * class.c (set_super_info): Inherit TYPE_DUMMY from sureclass. + (make_method_value): Also use index for interfaces. + (make_class_data): Skip dummy field for inherited data. + Don't build method array for dummy methods. + Set size_in_byte to -1 when using inirect dispatch + Don't build a hard class ref if we don't have a hard ref to our + superclass, or if we're using inirect dispatch. + Null out dispatch tables. + + (layout_class_method): Don't complain about non-static method + overrides static method is method is artificial. + + (build_static_field_ref): Disable atable references to static + fields for the time being. + + (layout_class_methods): Check for CLASS_INTERFACE as + well as CLASS_ABSTRACT. + 2004-11-24 Steven Bosscher <stevenb@suse.de> * class.c (make_class_data): Don't check flag_inline_functions. diff --git a/gcc/java/Make-lang.in b/gcc/java/Make-lang.in index bea8c73..16094b9 100644 --- a/gcc/java/Make-lang.in +++ b/gcc/java/Make-lang.in @@ -102,6 +102,7 @@ gt-java-builtins.h gtype-java.h gt-java-resource.h : s-gtype ; @true # Executables built by this Makefile: JAVA_OBJS = java/parse.o java/class.o java/decl.o java/expr.o \ java/constants.o java/lang.o java/typeck.o java/except.o java/verify.o \ + java/verify-glue.o java/verify-impl.o \ java/zextract.o java/jcf-io.o java/win32-host.o java/jcf-parse.o java/mangle.o \ java/mangle_name.o java/builtins.o java/resource.o \ java/jcf-write.o java/buffer.o java/check-init.o java/jcf-depend.o \ @@ -338,6 +339,10 @@ java/win32-host.o: java/win32-host.c $(CONFIG_H) $(SYSTEM_H) coretypes.h java/jc java/verify.o: java/verify.c $(CONFIG_H) $(JAVA_TREE_H) java/jcf.h \ java/javaop.h java/java-opcodes.h java/java-except.h toplev.h $(SYSTEM_H) \ coretypes.h $(TM_H) +java/verify-glue.o: java/verify-glue.c $(CONFIG_H) $(SYSTEM_H) $(JAVA_TREE_H) \ + coretypes.h $(TM_H) java/verify.h +java/verify-impl.o: java/verify-impl.c $(CONFIG_H) java/verify.h $(SYSTEM_H) \ + coretypes.h java/jcf.h $(JAVA_TREE_H) java/xref.o: java/xref.c java/xref.h $(CONFIG_H) $(JAVA_TREE_H) toplev.h \ $(SYSTEM_H) coretypes.h $(TM_H) java/zextract.o: java/zextract.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ diff --git a/gcc/java/check-init.c b/gcc/java/check-init.c index dacc4b9..05692b0 100644 --- a/gcc/java/check-init.c +++ b/gcc/java/check-init.c @@ -36,7 +36,7 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ /* The basic idea is that we assign each local variable declaration and each blank final field an index, and then we pass around bitstrings, where the (2*i)'th bit is set if decl whose DECL_BIT_INDEX - is i is definitely assigned, and the (2*i+1)'th bit is set if + is i is definitely assigned, and the the (2*i=1)'th bit is set if decl whose DECL_BIT_INDEX is i is definitely unassigned */ /* One segment of a bitstring. */ @@ -45,7 +45,7 @@ typedef unsigned int word; /* Pointer to a bitstring. */ typedef word *words; -/* Number of local variables currently active. */ +/* Number of locals variables currently active. */ static int num_current_locals = 0; /* The value of num_current_locals when we entered the closest @@ -191,6 +191,50 @@ get_variable_decl (tree exp) return op1; } } + else if (TREE_CODE (exp) == INDIRECT_REF) + { + /* For indirect dispatch, look for an expression of the form + (indirect_ref (+ (array_ref otable <N>) this)). + FIXME: it would probably be better to generate a JAVA_FIELD_REF + expression that gets converted to OTABLE access at + gimplification time. */ + exp = TREE_OPERAND (exp, 0); + if (TREE_CODE (exp) == PLUS_EXPR) + { + tree op0 = TREE_OPERAND (exp, 0); + STRIP_NOPS (op0); + if (TREE_CODE (op0) == ARRAY_REF) + { + tree table = TREE_OPERAND (op0, 0); + if (TREE_CODE (table) == VAR_DECL + && DECL_LANG_SPECIFIC (table) + && DECL_OWNER (table) + && TYPE_OTABLE_DECL (DECL_OWNER (table)) == table) + { + HOST_WIDE_INT index + = TREE_INT_CST_LOW (TREE_OPERAND (op0, 1)); + tree otable_methods + = TYPE_OTABLE_METHODS (DECL_OWNER (table)); + tree element; + for (element = otable_methods; + element; + element = TREE_CHAIN (element)) + { + if (index == 1) + { + tree purpose = TREE_PURPOSE (element); + if (TREE_CODE (purpose) == FIELD_DECL) + return purpose; + else + return NULL_TREE; + } + --index; + } + } + } + } + } + return NULL_TREE; } @@ -306,7 +350,7 @@ check_bool2_init (enum tree_code code, tree exp0, tree exp1, /* Check a boolean expression EXP for definite [un]assignment. BEFORE is the set of variables definitely [un]assigned before the conditional. (This bitstring may be modified arbitrarily in this function.) - On output, WHEN_FALSE is the set of variables definitely [un]assigned after + On output, WHEN_FALSE is the set of variables [un]definitely assigned after the conditional when the conditional is false. On output, WHEN_TRUE is the set of variables definitely [un]assigned after the conditional when the conditional is true. @@ -432,8 +476,8 @@ done_alternative (words after, struct alternatives *current) WORDS_NEEDED (2 * current->num_locals)); } -/* Used when we are done with a control flow branch and are all merged again. - AFTER is the merged state of [un]assigned variables, +/* Used when we done with a control flow branch and are all merged again. + * AFTER is the merged state of [un]assigned variables, CURRENT is a struct alt that was passed to BEGIN_ALTERNATIVES. */ #define END_ALTERNATIVES(after, current) \ @@ -445,7 +489,7 @@ done_alternative (words after, struct alternatives *current) start_current_locals = current.save_start_current_locals; \ } -/* Check for [un]initialized local variables in EXP. */ +/* Check for (un)initialized local variables in EXP. */ static void check_init (tree exp, words before) @@ -460,7 +504,7 @@ check_init (tree exp, words before) && DECL_NAME (exp) != this_identifier_node) { int index = DECL_BIT_INDEX (exp); - /* We don't want to report and mark as non-initialized class + /* We don't want to report and mark as non initialized class initialization flags. */ if (! LOCAL_CLASS_INITIALIZATION_FLAG_P (exp) && index >= 0 && ! ASSIGNED_P (before, index)) @@ -604,7 +648,7 @@ check_init (tree exp, words before) "hypothetical" analysis model. We do something much simpler: We just disallow assignments inside loops to final variables declared outside the loop. This means we may - disallow some contrived assignments that the JLS allows, but I + disallow some contrived assignments that the JLS, but I can't see how anything except a very contrived testcase (a do-while whose condition is false?) would care. */ diff --git a/gcc/java/class.c b/gcc/java/class.c index 543bdf2..391752a 100644 --- a/gcc/java/class.c +++ b/gcc/java/class.c @@ -43,6 +43,7 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ #include "stdio.h" #include "target.h" #include "except.h" +#include "cgraph.h" #include "tree-iterator.h" /* DOS brain-damage */ @@ -61,6 +62,7 @@ static tree maybe_layout_super_class (tree, tree); static void add_miranda_methods (tree, tree); static int assume_compiled (const char *); static tree build_symbol_entry (tree); +static tree emit_assertion_table (tree); struct obstack temporary_obstack; @@ -342,6 +344,34 @@ unmangle_classname (const char *name, int name_length) return to_return; } +#define GEN_TABLE(TABLE, NAME, TABLE_TYPE, TYPE) \ +do \ +{ \ + const char *typename = IDENTIFIER_POINTER (mangled_classname ("", TYPE)); \ + char *buf = alloca (strlen (typename) + strlen (#NAME "_syms_") + 1); \ + tree decl; \ + \ + sprintf (buf, #NAME "_%s", typename); \ + TYPE_## TABLE ##_DECL (type) = decl = \ + build_decl (VAR_DECL, get_identifier (buf), TABLE_TYPE); \ + DECL_EXTERNAL (decl) = 1; \ + TREE_STATIC (decl) = 1; \ + TREE_READONLY (decl) = 1; \ + TREE_CONSTANT (decl) = 1; \ + DECL_IGNORED_P (decl) = 1; \ + /* Mark the table as belonging to this class. */ \ + pushdecl (decl); \ + MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC (decl); \ + DECL_OWNER (decl) = TYPE; \ + sprintf (buf, #NAME "_syms_%s", typename); \ + TYPE_## TABLE ##_SYMS_DECL (TYPE) = \ + build_decl (VAR_DECL, get_identifier (buf), symbols_array_type); \ + TREE_STATIC (TYPE_## TABLE ##_SYMS_DECL (TYPE)) = 1; \ + TREE_CONSTANT (TYPE_## TABLE ##_SYMS_DECL (TYPE)) = 1; \ + DECL_IGNORED_P (TYPE_## TABLE ##_SYMS_DECL (TYPE)) = 1; \ + pushdecl (TYPE_## TABLE ##_SYMS_DECL (TYPE)); \ +} \ +while (0) /* Given a class, create the DECLs for all its associated indirect dispatch tables. */ @@ -372,54 +402,14 @@ gen_indirect_dispatch_tables (tree type) if (flag_indirect_dispatch) { - { - char *buf = alloca (strlen (typename) + strlen ("_otable_syms_") + 1); - - sprintf (buf, "_otable_%s", typename); - TYPE_OTABLE_DECL (type) = - build_decl (VAR_DECL, get_identifier (buf), otable_type); - DECL_EXTERNAL (TYPE_OTABLE_DECL (type)) = 1; - TREE_STATIC (TYPE_OTABLE_DECL (type)) = 1; - TREE_READONLY (TYPE_OTABLE_DECL (type)) = 1; - TREE_CONSTANT (TYPE_OTABLE_DECL (type)) = 1; - DECL_IGNORED_P (TYPE_OTABLE_DECL (type)) = 1; - pushdecl (TYPE_OTABLE_DECL (type)); - sprintf (buf, "_otable_syms_%s", typename); - TYPE_OTABLE_SYMS_DECL (type) = - build_decl (VAR_DECL, get_identifier (buf), symbols_array_type); - TREE_STATIC (TYPE_OTABLE_SYMS_DECL (type)) = 1; - TREE_CONSTANT (TYPE_OTABLE_SYMS_DECL (type)) = 1; - DECL_IGNORED_P(TYPE_OTABLE_SYMS_DECL (type)) = 1; - pushdecl (TYPE_OTABLE_SYMS_DECL (type)); - } - - { - char *buf = alloca (strlen (typename) + strlen ("_atable_syms_") + 1); - tree decl; - - sprintf (buf, "_atable_%s", typename); - TYPE_ATABLE_DECL (type) = decl = - build_decl (VAR_DECL, get_identifier (buf), atable_type); - DECL_EXTERNAL (decl) = 1; - TREE_STATIC (decl) = 1; - TREE_READONLY (decl) = 1; - TREE_CONSTANT (decl) = 1; - DECL_IGNORED_P (decl) = 1; - /* Mark the atable as belonging to this class. */ - pushdecl (decl); - MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC (decl); - DECL_OWNER (decl) = type; - sprintf (buf, "_atable_syms_%s", typename); - TYPE_ATABLE_SYMS_DECL (type) = - build_decl (VAR_DECL, get_identifier (buf), symbols_array_type); - TREE_STATIC (TYPE_ATABLE_SYMS_DECL (type)) = 1; - TREE_CONSTANT (TYPE_ATABLE_SYMS_DECL (type)) = 1; - DECL_IGNORED_P (TYPE_ATABLE_SYMS_DECL (type)) = 1; - pushdecl (TYPE_ATABLE_SYMS_DECL (type)); - } + GEN_TABLE (ATABLE, _atable, atable_type, type); + GEN_TABLE (OTABLE, _otable, otable_type, type); + GEN_TABLE (ITABLE, _itable, itable_type, type); } } +#undef GEN_TABLE + tree push_class (tree class_type, tree class_name) { @@ -959,7 +949,7 @@ build_class_ref (tree type) we always emit this hard superclass reference. */ if (flag_indirect_dispatch && type != output_class - && type != CLASSTYPE_SUPER (output_class) +// && type != CLASSTYPE_SUPER (output_class) && TREE_CODE (type) == RECORD_TYPE) return build_indirect_class_ref (type); @@ -1062,8 +1052,7 @@ build_static_field_ref (tree fdecl) However, currently sometimes gcj is too eager and will end up returning the field itself, leading to an incorrect external reference being generated. */ - if ((is_compiled - && (! flag_indirect_dispatch || current_class == fclass)) + if ((is_compiled && !flag_indirect_dispatch) || (FIELD_FINAL (fdecl) && DECL_INITIAL (fdecl) != NULL_TREE && (JSTRING_TYPE_P (TREE_TYPE (fdecl)) || JNUMERIC_TYPE_P (TREE_TYPE (fdecl))) @@ -1084,16 +1073,19 @@ build_static_field_ref (tree fdecl) = build_int_cst (NULL_TREE, get_symbol_table_index (fdecl, &TYPE_ATABLE_METHODS (output_class))); tree field_address - = build4 (ARRAY_REF, build_pointer_type (TREE_TYPE (fdecl)), + = build4 (ARRAY_REF, + TREE_TYPE (TREE_TYPE (TYPE_ATABLE_DECL (output_class))), TYPE_ATABLE_DECL (output_class), table_index, NULL_TREE, NULL_TREE); + field_address = convert (build_pointer_type (TREE_TYPE (fdecl)), + field_address); return fold (build1 (INDIRECT_REF, TREE_TYPE (fdecl), field_address)); } else { /* Compile as: - * *(FTYPE*)build_class_ref(FCLASS)->fields[INDEX].info.addr */ + *(FTYPE*)build_class_ref(FCLASS)->fields[INDEX].info.addr */ tree ref = build_class_ref (fclass); tree fld; int field_index = 0; @@ -1117,7 +1109,7 @@ build_static_field_ref (tree fdecl) ref, build_int_cst (NULL_TREE, field_index))); ref = build1 (INDIRECT_REF, field_type_node, ref); ref = build3 (COMPONENT_REF, field_info_union_node, - ref, lookup_field (&field_type_node, info_ident), + ref, lookup_field (&field_type_node, info_ident), NULL_TREE); ref = build3 (COMPONENT_REF, ptr_type_node, ref, TREE_CHAIN (TYPE_FIELDS (field_info_union_node)), @@ -1284,7 +1276,7 @@ make_field_value (tree fdecl) ? TREE_CHAIN (TYPE_FIELDS (field_info_union_node)) : TYPE_FIELDS (field_info_union_node)), (FIELD_STATIC (fdecl) - ? build_address_of (build_static_field_ref (fdecl)) + ? build_address_of (fdecl) : byte_position (fdecl))))); FINISH_RECORD_CONSTRUCTOR (finit); @@ -1565,7 +1557,9 @@ make_class_data (tree type) /* Build Field array. */ field = TYPE_FIELDS (type); - if (DECL_NAME (field) == NULL_TREE) + while (field && DECL_ARTIFICIAL (field)) + field = TREE_CHAIN (field); /* Skip dummy fields. */ + if (field && DECL_NAME (field) == NULL_TREE) field = TREE_CHAIN (field); /* Skip dummy field for inherited data. */ for ( ; field != NULL_TREE; field = TREE_CHAIN (field)) { @@ -1620,6 +1614,11 @@ make_class_data (tree type) && ! flag_keep_inline_functions && optimize) continue; + /* Even if we have a decl, we don't necessaily have the code. + This can happen if we inherit a method from a superclass for + which we don't have a .class file. */ + if (METHOD_DUMMY (method)) + continue; init = make_method_value (method); method_count++; methods = tree_cons (NULL_TREE, init, methods); @@ -1663,10 +1662,8 @@ make_class_data (tree type) super = CLASSTYPE_SUPER (type); if (super == NULL_TREE) super = null_pointer_node; - else if (/* FIXME: we should also test for (! - flag_indirect_dispatch) here, but libgcj can't cope with - a symbolic reference a superclass in the class data. */ - assume_compiled (IDENTIFIER_POINTER (DECL_NAME (type_decl))) + else if (! flag_indirect_dispatch + && assume_compiled (IDENTIFIER_POINTER (DECL_NAME (type_decl))) && assume_compiled (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (super))))) super = build_class_ref (super); else @@ -1721,13 +1718,19 @@ make_class_data (tree type) = emit_symbol_table (DECL_NAME (TYPE_OTABLE_DECL (type)), TYPE_OTABLE_DECL (type), TYPE_OTABLE_METHODS (type), - TYPE_OTABLE_SYMS_DECL (type), integer_type_node); + TYPE_OTABLE_SYMS_DECL (type), integer_type_node, 1); TYPE_ATABLE_DECL (type) = emit_symbol_table (DECL_NAME (TYPE_ATABLE_DECL (type)), TYPE_ATABLE_DECL (type), TYPE_ATABLE_METHODS (type), - TYPE_ATABLE_SYMS_DECL (type), ptr_type_node); + TYPE_ATABLE_SYMS_DECL (type), ptr_type_node, 1); + + TYPE_ITABLE_DECL (type) + = emit_symbol_table + (DECL_NAME (TYPE_ITABLE_DECL (type)), + TYPE_ITABLE_DECL (type), TYPE_ITABLE_METHODS (type), + TYPE_ITABLE_SYMS_DECL (type), ptr_type_node, 2); } TYPE_CTABLE_DECL (type) = emit_catch_table (type); @@ -1765,8 +1768,13 @@ make_class_data (tree type) PUSH_FIELD_VALUE (cons, "fields", fields_decl == NULL_TREE ? null_pointer_node : build1 (ADDR_EXPR, field_ptr_type_node, fields_decl)); - PUSH_FIELD_VALUE (cons, "size_in_bytes", size_in_bytes (type)); - PUSH_FIELD_VALUE (cons, "field_count", + /* If we're using the binary compatibility ABI we don't know the + size until load time. */ + PUSH_FIELD_VALUE (cons, "size_in_bytes", + (flag_indirect_dispatch + ? integer_minus_one_node + : size_in_bytes (type))); + PUSH_FIELD_VALUE (cons, "field_count", build_int_cst (NULL_TREE, field_count)); PUSH_FIELD_VALUE (cons, "static_field_count", build_int_cst (NULL_TREE, static_field_count)); @@ -1810,6 +1818,21 @@ make_class_data (tree type) TREE_CONSTANT (TYPE_ATABLE_DECL (type)) = 1; TREE_INVARIANT (TYPE_ATABLE_DECL (type)) = 1; } + if (TYPE_ITABLE_METHODS(type) == NULL_TREE) + { + PUSH_FIELD_VALUE (cons, "itable", null_pointer_node); + PUSH_FIELD_VALUE (cons, "itable_syms", null_pointer_node); + } + else + { + PUSH_FIELD_VALUE (cons, "itable", + build1 (ADDR_EXPR, itable_ptr_type, TYPE_ITABLE_DECL (type))); + PUSH_FIELD_VALUE (cons, "itable_syms", + build1 (ADDR_EXPR, symbols_array_ptr_type, + TYPE_ITABLE_SYMS_DECL (type))); + TREE_CONSTANT (TYPE_ITABLE_DECL (type)) = 1; + TREE_INVARIANT (TYPE_ITABLE_DECL (type)) = 1; + } PUSH_FIELD_VALUE (cons, "catch_classes", build1 (ADDR_EXPR, ptr_type_node, TYPE_CTABLE_DECL (type))); @@ -1817,7 +1840,13 @@ make_class_data (tree type) PUSH_FIELD_VALUE (cons, "loader", null_pointer_node); PUSH_FIELD_VALUE (cons, "interface_count", build_int_cst (NULL_TREE, interface_len)); - PUSH_FIELD_VALUE (cons, "state", integer_zero_node); + PUSH_FIELD_VALUE + (cons, "state", + convert (byte_type_node, + build_int_cst (NULL_TREE, + flag_indirect_dispatch + ? JV_STATE_PRELOADING + : JV_STATE_COMPILED))); PUSH_FIELD_VALUE (cons, "thread", null_pointer_node); PUSH_FIELD_VALUE (cons, "depth", integer_zero_node); @@ -1825,9 +1854,23 @@ make_class_data (tree type) PUSH_FIELD_VALUE (cons, "idt", null_pointer_node); PUSH_FIELD_VALUE (cons, "arrayclass", null_pointer_node); PUSH_FIELD_VALUE (cons, "protectionDomain", null_pointer_node); + + { + tree assertion_table_ref; + if (TYPE_ASSERTIONS (type) == NULL) + assertion_table_ref = null_pointer_node; + else + assertion_table_ref = build1 (ADDR_EXPR, + build_pointer_type (assertion_table_type), + emit_assertion_table (type)); + + PUSH_FIELD_VALUE (cons, "assertion_table", assertion_table_ref); + } + PUSH_FIELD_VALUE (cons, "hack_signers", null_pointer_node); PUSH_FIELD_VALUE (cons, "chain", null_pointer_node); PUSH_FIELD_VALUE (cons, "aux_info", null_pointer_node); + PUSH_FIELD_VALUE (cons, "engine", null_pointer_node); FINISH_RECORD_CONSTRUCTOR (cons); @@ -1838,11 +1881,26 @@ make_class_data (tree type) DECL_ALIGN (decl) = 64; rest_of_decl_compilation (decl, 1, 0); + + TYPE_OTABLE_DECL (type) = NULL_TREE; + TYPE_ATABLE_DECL (type) = NULL_TREE; + TYPE_CTABLE_DECL (type) = NULL_TREE; } void finish_class (void) { + if (TYPE_VERIFY_METHOD (output_class)) + { + tree verify_method = TYPE_VERIFY_METHOD (output_class); + DECL_SAVED_TREE (verify_method) + = add_stmt_to_compound (DECL_SAVED_TREE (verify_method), void_type_node, + build (RETURN_EXPR, void_type_node, NULL)); + java_genericize (verify_method); + cgraph_finalize_function (verify_method, false); + TYPE_ASSERTIONS (current_class) = NULL; + } + java_expand_catch_classes (current_class); current_function_decl = NULL_TREE; @@ -2095,20 +2153,22 @@ layout_class (tree this_class) if (!CLASS_FROM_SOURCE_P (this_class)) { int i; - - for (i = BINFO_N_BASE_BINFOS (TYPE_BINFO (this_class)) - 1; i > 0; i--) + if (TYPE_BINFO (this_class)) { - tree binfo = BINFO_BASE_BINFO (TYPE_BINFO (this_class), i); - tree super_interface = BINFO_TYPE (binfo); - tree maybe_super_interface - = maybe_layout_super_class (super_interface, NULL_TREE); - if (maybe_super_interface == NULL - || TREE_CODE (TYPE_SIZE (maybe_super_interface)) == ERROR_MARK) + for (i = BINFO_N_BASE_BINFOS (TYPE_BINFO (this_class)) - 1; i > 0; i--) { - TYPE_SIZE (this_class) = error_mark_node; - CLASS_BEING_LAIDOUT (this_class) = 0; - class_list = TREE_CHAIN (class_list); - return; + tree binfo = BINFO_BASE_BINFO (TYPE_BINFO (this_class), i); + tree super_interface = BINFO_TYPE (binfo); + tree maybe_super_interface + = maybe_layout_super_class (super_interface, NULL_TREE); + if (maybe_super_interface == NULL + || TREE_CODE (TYPE_SIZE (maybe_super_interface)) == ERROR_MARK) + { + TYPE_SIZE (this_class) = error_mark_node; + CLASS_BEING_LAIDOUT (this_class) = 0; + class_list = TREE_CHAIN (class_list); + return; + } } } } @@ -2124,8 +2184,11 @@ layout_class (tree this_class) static void add_miranda_methods (tree base_class, tree search_class) { - tree binfo, base_binfo; int i; + tree binfo, base_binfo; + + if (!CLASS_PARSED_P (search_class)) + load_class (search_class, 1); for (binfo = TYPE_BINFO (search_class), i = 1; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++) @@ -2133,6 +2196,11 @@ add_miranda_methods (tree base_class, tree search_class) tree method_decl; tree elt = BINFO_TYPE (base_binfo); + /* FIXME: This is totally bogus. We should not be handling + Miranda methods at all if we're using the BC ABI. */ + if (TYPE_DUMMY (elt)) + continue; + /* Ensure that interface methods are seen in declared order. */ if (!CLASS_LOADED_P (elt)) load_class (elt, 1); @@ -2193,7 +2261,8 @@ layout_class_methods (tree this_class) dtable_count = integer_zero_node; type_name = TYPE_NAME (this_class); - if (CLASS_ABSTRACT (type_name) || CLASS_INTERFACE (type_name)) + if (!flag_indirect_dispatch + && (CLASS_ABSTRACT (type_name) || CLASS_INTERFACE (type_name))) { /* An abstract class can have methods which are declared only in an implemented interface. These are called "Miranda @@ -2273,7 +2342,9 @@ layout_class_method (tree this_class, tree super_class, bool method_override = false; tree super_method = lookup_argument_method (super_class, method_name, method_sig); - if (super_method != NULL_TREE) + if (super_method != NULL_TREE + && ! METHOD_DUMMY (super_method) + && ! DECL_ARTIFICIAL (super_method)) { method_override = true; if (! METHOD_PUBLIC (super_method) && @@ -2296,11 +2367,19 @@ layout_class_method (tree this_class, tree super_class, error ("%Jnon-static method '%D' overrides static method", method_decl, method_decl); } - else if (! METHOD_FINAL (method_decl) - && ! METHOD_PRIVATE (method_decl) - && ! CLASS_FINAL (TYPE_NAME (this_class)) + else if (this_class == object_type_node + && (METHOD_FINAL (method_decl) + || METHOD_PRIVATE (method_decl))) + { + /* We don't generate vtable entries for final Object + methods. This is simply to save space, since every + object would otherwise have to define them. */ + } + else if (! METHOD_PRIVATE (method_decl) && dtable_count) { + /* We generate vtable entries for final methods because they + may one day be changed to non-final. */ set_method_index (method_decl, dtable_count); dtable_count = fold (build2 (PLUS_EXPR, integer_type_node, dtable_count, integer_one_node)); @@ -2387,14 +2466,20 @@ static tree build_symbol_entry (tree decl) { tree clname, name, signature, sym; - clname = build_utf8_ref (DECL_NAME (TYPE_NAME (DECL_CONTEXT (decl)))); - name = build_utf8_ref (DECL_NAME (decl)); + /* ??? Constructors are given the name foo.foo all the way through + the compiler, but in the method table they're all renamed + foo.<init>. So, we have to do the same here unless we want an + unresolved reference at runtime. */ + name = build_utf8_ref ((TREE_CODE (decl) == FUNCTION_DECL + && DECL_CONSTRUCTOR_P (decl)) + ? init_identifier_node + : DECL_NAME (decl)); signature = build_java_signature (TREE_TYPE (decl)); signature = build_utf8_ref (unmangle_classname (IDENTIFIER_POINTER (signature), IDENTIFIER_LENGTH (signature))); - + START_RECORD_CONSTRUCTOR (sym, symbol_type); PUSH_FIELD_VALUE (sym, "clname", clname); PUSH_FIELD_VALUE (sym, "name", name); @@ -2410,7 +2495,8 @@ build_symbol_entry (tree decl) tree emit_symbol_table (tree name, tree the_table, tree decl_list, - tree the_syms_decl, tree the_array_element_type) + tree the_syms_decl, tree the_array_element_type, + int element_size) { tree method_list, method, table, list, null_symbol; tree table_size, the_array_type; @@ -2457,7 +2543,8 @@ emit_symbol_table (tree name, tree the_table, tree decl_list, uninitialized static array of INDEX + 1 elements. The extra entry is used by the runtime to track whether the table has been initialized. */ - table_size = build_index_type (build_int_cst (NULL_TREE, index)); + table_size + = build_index_type (build_int_cst (NULL_TREE, index * element_size + 1)); the_array_type = build_array_type (the_array_element_type, table_size); the_table = build_decl (VAR_DECL, name, the_array_type); TREE_STATIC (the_table) = 1; @@ -2467,7 +2554,7 @@ emit_symbol_table (tree name, tree the_table, tree decl_list, return the_table; } -/* make an entry for the catch_classes list. */ +/* Make an entry for the catch_classes list. */ tree make_catch_class_record (tree catch_class, tree classname) { @@ -2512,7 +2599,95 @@ emit_catch_table (tree this_class) rest_of_decl_compilation (table, 1, 0); return table; } - + +/* Given a type, return the signature used by + _Jv_FindClassFromSignature() in libgcj. This isn't exactly the + same as build_java_signature() because we want the canonical array + type. */ + +static tree +build_signature_for_libgcj (tree type) +{ + tree sig, ref; + + sig = build_java_signature (type); + ref = build_utf8_ref (unmangle_classname (IDENTIFIER_POINTER (sig), + IDENTIFIER_LENGTH (sig))); + return ref; +} + +/* Add an entry to the type assertion table. Callback used during hashtable + traversal. */ + +static int +add_assertion_table_entry (void **htab_entry, void *ptr) +{ + tree entry; + tree code_val, op1_utf8, op2_utf8; + tree *list = (tree *) ptr; + type_assertion *as = (type_assertion *) *htab_entry; + + code_val = build_int_cst (NULL_TREE, as->assertion_code); + + if (as->op1 == NULL_TREE) + op1_utf8 = null_pointer_node; + else + op1_utf8 = build_signature_for_libgcj (as->op1); + + if (as->op2 == NULL_TREE) + op2_utf8 = null_pointer_node; + else + op2_utf8 = build_signature_for_libgcj (as->op2); + + START_RECORD_CONSTRUCTOR (entry, assertion_entry_type); + PUSH_FIELD_VALUE (entry, "assertion_code", code_val); + PUSH_FIELD_VALUE (entry, "op1", op1_utf8); + PUSH_FIELD_VALUE (entry, "op2", op2_utf8); + FINISH_RECORD_CONSTRUCTOR (entry); + + *list = tree_cons (NULL_TREE, entry, *list); + return true; +} + +/* Generate the type assertion table for CLASS, and return its DECL. */ + +static tree +emit_assertion_table (tree class) +{ + tree null_entry, ctor, table_decl; + tree list = NULL_TREE; + htab_t assertions_htab = TYPE_ASSERTIONS (class); + + /* Iterate through the hash table. */ + htab_traverse (assertions_htab, add_assertion_table_entry, &list); + + /* Finish with a null entry. */ + START_RECORD_CONSTRUCTOR (null_entry, assertion_entry_type); + PUSH_FIELD_VALUE (null_entry, "assertion_code", integer_zero_node); + PUSH_FIELD_VALUE (null_entry, "op1", null_pointer_node); + PUSH_FIELD_VALUE (null_entry, "op2", null_pointer_node); + FINISH_RECORD_CONSTRUCTOR (null_entry); + + list = tree_cons (NULL_TREE, null_entry, list); + + /* Put the list in the right order and make it a constructor. */ + list = nreverse (list); + ctor = build_constructor (assertion_table_type, list); + + table_decl = build_decl (VAR_DECL, mangled_classname ("_type_assert_", class), + assertion_table_type); + + TREE_STATIC (table_decl) = 1; + TREE_READONLY (table_decl) = 1; + TREE_CONSTANT (table_decl) = 1; + DECL_IGNORED_P (table_decl) = 1; + + DECL_INITIAL (table_decl) = ctor; + DECL_ARTIFICIAL (table_decl) = 1; + rest_of_decl_compilation (table_decl, 1, 0); + + return table_decl; +} void init_class_processing (void) diff --git a/gcc/java/decl.c b/gcc/java/decl.c index 4731022..54f788e 100644 --- a/gcc/java/decl.c +++ b/gcc/java/decl.c @@ -242,17 +242,44 @@ check_local_unnamed_variable (tree best, tree decl, tree type) || (INTEGRAL_TYPE_P (decl_type) && INTEGRAL_TYPE_P (type) && TYPE_PRECISION (decl_type) <= 32 - && TYPE_PRECISION (type) <= 32 + && TYPE_PRECISION (type) <= 32 && TYPE_PRECISION (decl_type) >= TYPE_PRECISION (type)) - || (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE - && type == ptr_type_node)) - { - if (best == NULL_TREE + /* ptr_type_node is used for null pointers, which are + assignment compatible with everything. */ + || (TREE_CODE (decl_type) == POINTER_TYPE + && type == ptr_type_node) + /* Whenever anyone wants to use a slot that is initially + occupied by a PARM_DECL of pointer type they must get that + decl, even if they asked for a pointer to a different type. + However, if someone wants a scalar variable in a slot that + initially held a pointer arg -- or vice versa -- we create a + new VAR_DECL. + + ???: As long as verification is correct, this will be a + compatible type. But maybe we should create a dummy vribale + and replace all references to it with the DECL and a + NOP_EXPR. + */ + || (TREE_CODE (decl_type) == POINTER_TYPE + && TREE_CODE (decl) == PARM_DECL + && TREE_CODE (type) == POINTER_TYPE) + + /* The new verifier requires a similar treatment in the + situation where the parameter has an integral type which + promotes to `int'. */ + || (flag_new_verifier + && TREE_CODE (decl) == PARM_DECL + && INTEGRAL_TYPE_P (decl_type) + && TYPE_PRECISION (decl_type) <= 32 + && INTEGRAL_TYPE_P (type) + && TYPE_PRECISION (type) <= 32)) + { + if (best == NULL_TREE || (decl_type == type && TREE_TYPE (best) != type)) - return decl; - } + return decl; + } - return best; + return best; } @@ -286,9 +313,9 @@ find_local_variable (int index, tree type, int pc ATTRIBUTE_UNUSED) variable that is used for every reference in that local variable slot. */ if (! decl) - { - char buf[64]; - tree name; + { + char buf[64]; + tree name; sprintf (buf, "#slot#%d#%d", index, uniq++); name = get_identifier (buf); decl = build_decl (VAR_DECL, name, type); @@ -688,6 +715,11 @@ java_init_decl_processing (void) TYPE_NONALIASED_COMPONENT (atable_type) = 1; atable_ptr_type = build_pointer_type (atable_type); + itable_type = build_array_type (ptr_type_node, + one_elt_array_domain_type); + TYPE_NONALIASED_COMPONENT (itable_type) = 1; + itable_ptr_type = build_pointer_type (itable_type); + symbol_type = make_node (RECORD_TYPE); PUSH_FIELD (symbol_type, field, "clname", utf8const_ptr_type); PUSH_FIELD (symbol_type, field, "name", utf8const_ptr_type); @@ -698,6 +730,15 @@ java_init_decl_processing (void) one_elt_array_domain_type); symbols_array_ptr_type = build_pointer_type (symbols_array_type); + assertion_entry_type = make_node (RECORD_TYPE); + PUSH_FIELD (assertion_entry_type, field, "assertion_code", integer_type_node); + PUSH_FIELD (assertion_entry_type, field, "op1", utf8const_ptr_type); + PUSH_FIELD (assertion_entry_type, field, "op2", utf8const_ptr_type); + FINISH_RECORD (assertion_entry_type); + + assertion_table_type = build_array_type (assertion_entry_type, + one_elt_array_domain_type); + /* As you're adding items here, please update the code right after this section, so that the filename containing the source code of the pre-defined class gets registered correctly. */ @@ -813,6 +854,9 @@ java_init_decl_processing (void) PUSH_FIELD (class_type_node, field, "atable", atable_ptr_type); PUSH_FIELD (class_type_node, field, "atable_syms", symbols_array_ptr_type); + PUSH_FIELD (class_type_node, field, "itable", itable_ptr_type); + PUSH_FIELD (class_type_node, field, "itable_syms", + symbols_array_ptr_type); PUSH_FIELD (class_type_node, field, "catch_classes", ptr_type_node); PUSH_FIELD (class_type_node, field, "interfaces", build_pointer_type (class_ptr_type)); @@ -825,9 +869,11 @@ java_init_decl_processing (void) PUSH_FIELD (class_type_node, field, "idt", ptr_type_node); PUSH_FIELD (class_type_node, field, "arrayclass", ptr_type_node); PUSH_FIELD (class_type_node, field, "protectionDomain", ptr_type_node); + PUSH_FIELD (class_type_node, field, "assertion_table", ptr_type_node); PUSH_FIELD (class_type_node, field, "hack_signers", ptr_type_node); PUSH_FIELD (class_type_node, field, "chain", ptr_type_node); PUSH_FIELD (class_type_node, field, "aux_info", ptr_type_node); + PUSH_FIELD (class_type_node, field, "engine", ptr_type_node); for (t = TYPE_FIELDS (class_type_node); t != NULL_TREE; t = TREE_CHAIN (t)) FIELD_PRIVATE (t) = 1; push_super_field (class_type_node, object_type_node); @@ -993,8 +1039,14 @@ java_init_decl_processing (void) = builtin_function ("_Jv_LookupInterfaceMethodIdx", build_function_type (ptr_type_node, t), 0, NOT_BUILT_IN, NULL, NULL_TREE); - DECL_IS_PURE (soft_lookupinterfacemethod_node) = 1; + t = tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, ptr_type_node, endlink))); + soft_lookupinterfacemethodbyname_node + = builtin_function ("_Jv_LookupInterfaceMethod", + build_function_type (ptr_type_node, t), + 0, NOT_BUILT_IN, NULL, NULL_TREE); t = tree_cons (NULL_TREE, object_ptr_type_node, tree_cons (NULL_TREE, ptr_type_node, tree_cons (NULL_TREE, ptr_type_node, diff --git a/gcc/java/expr.c b/gcc/java/expr.c index deb75a9..305ab5b 100644 --- a/gcc/java/expr.c +++ b/gcc/java/expr.c @@ -323,26 +323,63 @@ pop_type_0 (tree type, char **messagep) t = stack_type_map[--stack_pointer]; if (type == NULL_TREE || t == type) return t; + if (TREE_CODE (t) == TREE_LIST) + { + do + { + tree tt = TREE_PURPOSE (t); + if (! can_widen_reference_to (tt, type)) + { + t = tt; + goto fail; + } + t = TREE_CHAIN (t); + } + while (t); + return t; + } if (INTEGRAL_TYPE_P (type) && INTEGRAL_TYPE_P (t) && TYPE_PRECISION (type) <= 32 && TYPE_PRECISION (t) <= 32) - return t; + return t; if (TREE_CODE (type) == POINTER_TYPE && TREE_CODE (t) == POINTER_TYPE) { - if (type == ptr_type_node || type == object_ptr_type_node) - return t; - else if (t == ptr_type_node) /* Special case for null reference. */ - return type; - else if (can_widen_reference_to (t, type)) - return t; - /* This is a kludge, but matches what Sun's verifier does. - It can be tricked, but is safe as long as type errors - (i.e. interface method calls) are caught at run-time. */ - else if (CLASS_INTERFACE (TYPE_NAME (TREE_TYPE (type)))) - return object_ptr_type_node; + if (flag_new_verifier) + { + /* Since the verifier has already run, we know that any + types we see will be compatible. In BC mode, this fact + may be checked at runtime, but if that is so then we can + assume its truth here as well. So, we always succeed + here, with the expected type. */ + return type; + } + else + { + if (type == ptr_type_node || type == object_ptr_type_node) + return t; + else if (t == ptr_type_node) /* Special case for null reference. */ + return type; + /* This is a kludge, but matches what Sun's verifier does. + It can be tricked, but is safe as long as type errors + (i.e. interface method calls) are caught at run-time. */ + else if (CLASS_INTERFACE (TYPE_NAME (TREE_TYPE (type)))) + return object_ptr_type_node; + else if (can_widen_reference_to (t, type)) + return t; + } + } + + if (! flag_verify_invocations && flag_indirect_dispatch + && t == object_ptr_type_node) + { + if (type != ptr_type_node) + warning ("need to insert runtime check for %s", + xstrdup (lang_printable_name (type, 0))); + return type; } /* lang_printable_name uses a static buffer, so we must save the result from calling it the first time. */ + fail: { char *temp = xstrdup (lang_printable_name (type, 0)); *messagep = concat ("expected type '", temp, @@ -370,6 +407,68 @@ pop_type (tree type) return type; } + +/* Return true if two type assertions are equal. */ + +static int +type_assertion_eq (const void * k1_p, const void * k2_p) +{ + type_assertion k1 = *(type_assertion *)k1_p; + type_assertion k2 = *(type_assertion *)k2_p; + return (k1.assertion_code == k2.assertion_code + && k1.op1 == k2.op1 + && k1.op2 == k2.op2); +} + +/* Hash a type assertion. */ + +static hashval_t +type_assertion_hash (const void *p) +{ + const type_assertion *k_p = p; + hashval_t hash = iterative_hash (&k_p->assertion_code, sizeof + k_p->assertion_code, 0); + hash = iterative_hash (&k_p->op1, sizeof k_p->op1, hash); + return iterative_hash (&k_p->op2, sizeof k_p->op2, hash); +} + +/* Add an entry to the type assertion table for the given class. + CLASS is the class for which this assertion will be evaluated by the + runtime during loading/initialization. + ASSERTION_CODE is the 'opcode' or type of this assertion: see java-tree.h. + OP1 and OP2 are the operands. The tree type of these arguments may be + specific to each assertion_code. */ + +void +add_type_assertion (tree class, int assertion_code, tree op1, tree op2) +{ + htab_t assertions_htab; + type_assertion as; + void **as_pp; + + assertions_htab = TYPE_ASSERTIONS (class); + if (assertions_htab == NULL) + { + assertions_htab = htab_create_ggc (7, type_assertion_hash, + type_assertion_eq, NULL); + TYPE_ASSERTIONS (current_class) = assertions_htab; + } + + as.assertion_code = assertion_code; + as.op1 = op1; + as.op2 = op2; + + as_pp = htab_find_slot (assertions_htab, &as, true); + + /* Don't add the same assertion twice. */ + if (*as_pp) + return; + + *as_pp = ggc_alloc (sizeof (type_assertion)); + **(type_assertion **)as_pp = as; +} + + /* Return 1 if SOURCE_TYPE can be safely widened to TARGET_TYPE. Handles array types and interfaces. */ @@ -387,6 +486,28 @@ can_widen_reference_to (tree source_type, tree target_type) if (source_type == target_type) return 1; + + /* FIXME: This is very pessimistic, in that it checks everything, + even if we already know that the types are compatible. If we're + to support full Java class loader semantics, we need this. + However, we could do something more optimal. */ + if (! flag_verify_invocations) + { + add_type_assertion (current_class, JV_ASSERT_TYPES_COMPATIBLE, + source_type, target_type); + + if (!quiet_flag) + warning ("assert: %s is assign compatible with %s", + xstrdup (lang_printable_name (target_type, 0)), + xstrdup (lang_printable_name (source_type, 0))); + /* Punt everything to runtime. */ + return 1; + } + + if (TYPE_DUMMY (source_type) || TYPE_DUMMY (target_type)) + { + return 1; + } else { if (TYPE_ARRAY_P (source_type) || TYPE_ARRAY_P (target_type)) @@ -420,7 +541,16 @@ can_widen_reference_to (tree source_type, tree target_type) int source_depth = class_depth (source_type); int target_depth = class_depth (target_type); - /* class_depth can return a negative depth if an error occurred */ + if (TYPE_DUMMY (source_type) || TYPE_DUMMY (target_type)) + { + if (! quiet_flag) + warning ("assert: %s is assign compatible with %s", + xstrdup (lang_printable_name (target_type, 0)), + xstrdup (lang_printable_name (source_type, 0))); + return 1; + } + + /* class_depth can return a negative depth if an error occurred */ if (source_depth < 0 || target_depth < 0) return 0; @@ -687,13 +817,18 @@ build_java_array_length_access (tree node) throws a NullPointerException. The only way we could get a node of type ptr_type_node at this point is `aconst_null; arraylength' or something equivalent. */ - if (type == ptr_type_node) + if (!flag_new_verifier && type == ptr_type_node) return build3 (CALL_EXPR, int_type_node, build_address_of (soft_nullpointer_node), NULL_TREE, NULL_TREE); if (!is_array_type_p (type)) - abort (); + { + /* With the new verifier, we will see an ordinary pointer type + here. In this case, we just use an arbitrary array type. */ + array_type = build_java_array_type (object_ptr_type_node, -1); + type = promote_type (array_type); + } length = java_array_type_length (type); if (length >= 0) @@ -754,6 +889,13 @@ build_java_arrayaccess (tree array, tree type, tree index) tree ref; tree array_type = TREE_TYPE (TREE_TYPE (array)); + if (!is_array_type_p (TREE_TYPE (array))) + { + /* With the new verifier, we will see an ordinary pointer type + here. In this case, we just use the correct array type. */ + array_type = build_java_array_type (type, -1); + } + if (flag_bounds_check) { /* Generate: @@ -803,11 +945,21 @@ build_java_arraystore_check (tree array, tree object) tree array_type_p = TREE_TYPE (array); tree object_type = TYPE_NAME (TREE_TYPE (TREE_TYPE (object))); - if (! is_array_type_p (array_type_p)) - abort (); + if (! flag_verify_invocations) + { + /* With the new verifier, we don't track precise types. FIXME: + performance regression here. */ + element_type = TYPE_NAME (object_type_node); + } + else + { + if (! is_array_type_p (array_type_p)) + abort (); - /* Get the TYPE_DECL for ARRAY's element type. */ - element_type = TYPE_NAME (TREE_TYPE (TREE_TYPE (TREE_TYPE (array_type_p)))); + /* Get the TYPE_DECL for ARRAY's element type. */ + element_type + = TYPE_NAME (TREE_TYPE (TREE_TYPE (TREE_TYPE (array_type_p)))); + } if (TREE_CODE (element_type) != TYPE_DECL || TREE_CODE (object_type) != TYPE_DECL) @@ -816,13 +968,14 @@ build_java_arraystore_check (tree array, tree object) if (!flag_store_check) return build1 (NOP_EXPR, array_type_p, array); - /* No check is needed if the element type is final or is itself an array. - Also check that element_type matches object_type, since in the bytecode - compilation case element_type may be the actual element type of the array - rather than its declared type. */ + /* No check is needed if the element type is final. Also check that + element_type matches object_type, since in the bytecode + compilation case element_type may be the actual element type of + the array rather than its declared type. However, if we're doing + indirect dispatch, we can't do the `final' optimization. */ if (element_type == object_type - && (TYPE_ARRAY_P (TREE_TYPE (element_type)) - || CLASS_FINAL (element_type))) + && ! flag_indirect_dispatch + && CLASS_FINAL (element_type)) return build1 (NOP_EXPR, array_type_p, array); /* OBJECT might be wrapped by a SAVE_EXPR. */ @@ -864,24 +1017,30 @@ build_java_arraystore_check (tree array, tree object) ARRAY_NODE. This function is used to retrieve something less vague than a pointer type when indexing the first dimension of something like [[<t>. May return a corrected type, if necessary, otherwise INDEXED_TYPE is - return unchanged. - As a side effect, it also makes sure that ARRAY_NODE is an array. */ + return unchanged. */ static tree build_java_check_indexed_type (tree array_node, tree indexed_type) { tree elt_type; + /* We used to check to see if ARRAY_NODE really had array type. + However, with the new verifier, this is not necessary, as we know + that the object will be an array of the appropriate type. */ + + if (flag_new_verifier) + return indexed_type; + if (!is_array_type_p (TREE_TYPE (array_node))) abort (); elt_type = (TYPE_ARRAY_ELEMENT (TREE_TYPE (TREE_TYPE (array_node)))); - if (indexed_type == ptr_type_node ) - return promote_type (elt_type); + if (indexed_type == ptr_type_node) + return promote_type (elt_type); /* BYTE/BOOLEAN store and load are used for both type */ - if (indexed_type == byte_type_node && elt_type == boolean_type_node ) + if (indexed_type == byte_type_node && elt_type == boolean_type_node) return boolean_type_node; if (indexed_type != elt_type ) @@ -992,7 +1151,25 @@ expand_java_arraystore (tree rhs_type_node) && TYPE_PRECISION (rhs_type_node) <= 32) ? int_type_node : rhs_type_node); tree index = pop_value (int_type_node); - tree array = pop_value (ptr_type_node); + tree array_type, array; + + if (flag_new_verifier) + { + /* If we're processing an `aaload' we might as well just pick + `Object'. */ + if (TREE_CODE (rhs_type_node) == POINTER_TYPE) + { + array_type = build_java_array_type (object_ptr_type_node, -1); + rhs_type_node = object_ptr_type_node; + } + else + array_type = build_java_array_type (rhs_type_node, -1); + } + else + array_type = ptr_type_node; + array = pop_value (array_type); + if (flag_new_verifier) + array = build1 (NOP_EXPR, promote_type (array_type), array); rhs_type_node = build_java_check_indexed_type (array, rhs_type_node); @@ -1019,25 +1196,45 @@ expand_java_arraystore (tree rhs_type_node) */ static void -expand_java_arrayload (tree lhs_type_node ) +expand_java_arrayload (tree lhs_type_node) { tree load_node; tree index_node = pop_value (int_type_node); - tree array_node = pop_value (ptr_type_node); + tree array_type; + tree array_node; + + if (flag_new_verifier) + { + /* If we're processing an `aaload' we might as well just pick + `Object'. */ + if (TREE_CODE (lhs_type_node) == POINTER_TYPE) + { + array_type = build_java_array_type (object_ptr_type_node, -1); + lhs_type_node = object_ptr_type_node; + } + else + array_type = build_java_array_type (lhs_type_node, -1); + } + else + array_type = ptr_type_node; + array_node = pop_value (array_type); + if (flag_new_verifier) + array_node = build1 (NOP_EXPR, promote_type (array_type), array_node); index_node = save_expr (index_node); array_node = save_expr (array_node); - + if (TREE_TYPE (array_node) == ptr_type_node) /* The only way we could get a node of type ptr_type_node at this point is `aconst_null; arraylength' or something equivalent, so - unconditionally throw NullPointerException. */ + unconditionally throw NullPointerException. */ load_node = build3 (CALL_EXPR, lhs_type_node, build_address_of (soft_nullpointer_node), NULL_TREE, NULL_TREE); else { - lhs_type_node = build_java_check_indexed_type (array_node, lhs_type_node); + lhs_type_node = build_java_check_indexed_type (array_node, + lhs_type_node); load_node = build_java_arrayaccess (array_node, lhs_type_node, index_node); @@ -1156,6 +1353,19 @@ class_has_finalize_method (tree type) return HAS_FINALIZER_P (type) || class_has_finalize_method (super); } +tree +java_create_object (tree type) +{ + tree alloc_node = (class_has_finalize_method (type) + ? alloc_object_node + : alloc_no_finalizer_node); + + return build (CALL_EXPR, promote_type (type), + build_address_of (alloc_node), + build_tree_list (NULL_TREE, build_class_ref (type)), + NULL_TREE); +} + static void expand_java_NEW (tree type) { @@ -1221,7 +1431,8 @@ build_instanceof (tree value, tree type) we only need to check for `null'. */ expr = build2 (NE_EXPR, itype, value, null_pointer_node); } - else if (! TYPE_ARRAY_P (type) + else if (flag_verify_invocations + && ! TYPE_ARRAY_P (type) && ! TYPE_ARRAY_P (valtype) && DECL_P (klass) && DECL_P (valclass) && ! CLASS_INTERFACE (valclass) @@ -1292,7 +1503,7 @@ expand_iinc (unsigned int local_var_index, int ival, int pc) update_aliases (local_var, local_var_index, pc); } - + tree build_java_soft_divmod (enum tree_code op, tree type, tree op1, tree op2) { @@ -1529,12 +1740,13 @@ build_field_ref (tree self_value, tree self_class, tree name) tree base_type = promote_type (base_class); if (base_type != TREE_TYPE (self_value)) self_value = fold (build1 (NOP_EXPR, base_type, self_value)); - if (flag_indirect_dispatch - && output_class != self_class) - /* FIXME: output_class != self_class is not exactly the right - test. What we really want to know is whether self_class is - in the same translation unit as output_class. If it is, - we can make a direct reference. */ + if (! flag_syntax_only + && (flag_indirect_dispatch + /* DECL_FIELD_OFFSET == 0 if we have no reference for + the field, perhaps because we couldn't find the class + in which the field is defined. + FIXME: We should investigate this. */ + || DECL_FIELD_OFFSET (field_decl) == 0)) { tree otable_index = build_int_cst (NULL_TREE, get_symbol_table_index @@ -1696,29 +1908,6 @@ expand_java_add_case (tree switch_expr, int match, int target_pc) append_to_statement_list (x, &SWITCH_BODY (switch_expr)); } -#if 0 -static void -expand_java_call (int target_pc, int return_address) -{ - tree target_label = lookup_label (target_pc); - tree value = build_int_cst (NULL_TREE, return_address); - push_value (value); - flush_quick_stack (); - expand_goto (target_label); -} - -static void -expand_java_ret (tree return_address ATTRIBUTE_UNUSED) -{ - warning ("ret instruction not implemented"); -#if 0 - tree target_label = lookup_label (target_pc); - flush_quick_stack (); - expand_goto (target_label); -#endif -} -#endif - static tree pop_arguments (tree arg_types) { @@ -1729,9 +1918,17 @@ pop_arguments (tree arg_types) tree tail = pop_arguments (TREE_CHAIN (arg_types)); tree type = TREE_VALUE (arg_types); tree arg = pop_value (type); - if (targetm.calls.promote_prototypes (type) - && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node) - && INTEGRAL_TYPE_P (type)) + + /* With the new verifier we simply cast each argument to its + proper type. This is needed since we lose type information + coming out of the verifier. We also have to do this with the + old verifier when we pop an integer type that must be + promoted for the function call. */ + if (flag_new_verifier && TREE_CODE (type) == POINTER_TYPE) + arg = build1 (NOP_EXPR, type, arg); + else if (targetm.calls.promote_prototypes (type) + && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node) + && INTEGRAL_TYPE_P (type)) arg = convert (integer_type_node, arg); return tree_cons (NULL_TREE, arg, tail); } @@ -1824,8 +2021,12 @@ build_known_method_ref (tree method, tree method_type ATTRIBUTE_UNUSED, tree func; if (is_compiled_class (self_type)) { - if (!flag_indirect_dispatch - || (!TREE_PUBLIC (method) && DECL_CONTEXT (method))) + /* With indirect dispatch we have to use indirect calls for all + publically visible methods or gcc will use PLT indirections + to reach them. We also have to use indirect dispatch for all + external methods. */ + if (! flag_indirect_dispatch + || (! DECL_EXTERNAL (method) && ! TREE_PUBLIC (method))) { make_decl_rtl (method); func = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (method)), @@ -1836,9 +2037,11 @@ build_known_method_ref (tree method, tree method_type ATTRIBUTE_UNUSED, tree table_index = build_int_cst (NULL_TREE, get_symbol_table_index (method, &TYPE_ATABLE_METHODS (output_class))); - func = build4 (ARRAY_REF, method_ptr_type_node, - TYPE_ATABLE_DECL (output_class), table_index, - NULL_TREE, NULL_TREE); + func + = build4 (ARRAY_REF, + TREE_TYPE (TREE_TYPE (TYPE_ATABLE_DECL (output_class))), + TYPE_ATABLE_DECL (output_class), table_index, + NULL_TREE, NULL_TREE); } func = convert (method_ptr_type_node, func); } @@ -1899,9 +2102,9 @@ invoke_build_dtable (int is_invoke_interface, tree arg_list) argument is an array then get the dispatch table of the class Object rather than the one from the objectref. */ objectref = (is_invoke_interface - && is_array_type_p (TREE_TYPE (TREE_VALUE (arg_list))) ? - object_type_node : TREE_VALUE (arg_list)); - + && is_array_type_p (TREE_TYPE (TREE_VALUE (arg_list))) + ? build_class_ref (object_type_node) : TREE_VALUE (arg_list)); + if (dtable_ident == NULL_TREE) dtable_ident = get_identifier ("vtable"); dtable = build_java_indirect_ref (object_type_node, objectref, @@ -1914,8 +2117,8 @@ invoke_build_dtable (int is_invoke_interface, tree arg_list) /* Determine the index in SYMBOL_TABLE for a reference to the decl T. If this decl has not been seen before, it will be added to the - otable_methods. If it has, the existing table slot will be - reused. */ + [oa]table_methods. If it has, the existing table slot will be + reused. */ int get_symbol_table_index (tree t, tree *symbol_table) @@ -1935,7 +2138,7 @@ get_symbol_table_index (tree t, tree *symbol_table) { tree value = TREE_VALUE (method_list); if (value == t) - return i; + return i; i++; if (TREE_CHAIN (method_list) == NULL_TREE) break; @@ -1958,6 +2161,9 @@ build_invokevirtual (tree dtable, tree method) if (flag_indirect_dispatch) { + if (CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (method)))) + abort (); + otable_index = build_int_cst (NULL_TREE, get_symbol_table_index (method, &TYPE_OTABLE_METHODS (output_class))); @@ -1997,11 +2203,8 @@ build_invokeinterface (tree dtable, tree method) tree lookup_arg; tree interface; tree idx; - tree otable_index; - /* We expand invokeinterface here. _Jv_LookupInterfaceMethod() will - ensure that the selected method exists, is public and not - abstract nor static. */ + /* We expand invokeinterface here. */ if (class_ident == NULL_TREE) class_ident = get_identifier ("class"); @@ -2018,28 +2221,42 @@ build_invokeinterface (tree dtable, tree method) if (flag_indirect_dispatch) { - otable_index - = build_int_cst (NULL_TREE, get_symbol_table_index - (method, &TYPE_OTABLE_METHODS (output_class))); - idx = build4 (ARRAY_REF, integer_type_node, - TYPE_OTABLE_DECL (output_class), otable_index, - NULL_TREE, NULL_TREE); + int itable_index + = 2 * (get_symbol_table_index + (method, &TYPE_ITABLE_METHODS (output_class))); + interface + = build4 (ARRAY_REF, + TREE_TYPE (TREE_TYPE (TYPE_ITABLE_DECL (output_class))), + TYPE_ITABLE_DECL (output_class), + build_int_cst (NULL_TREE, itable_index-1), + NULL_TREE, NULL_TREE); + idx + = build4 (ARRAY_REF, + TREE_TYPE (TREE_TYPE (TYPE_ITABLE_DECL (output_class))), + TYPE_ITABLE_DECL (output_class), + build_int_cst (NULL_TREE, itable_index), + NULL_TREE, NULL_TREE); + interface = convert (class_ptr_type, interface); + idx = convert (integer_type_node, idx); } else - idx = build_int_cst (NULL_TREE, - get_interface_method_index (method, interface)); - - lookup_arg = tree_cons (NULL_TREE, dtable, - tree_cons (NULL_TREE, build_class_ref (interface), - build_tree_list (NULL_TREE, idx))); + { + idx = build_int_cst (NULL_TREE, + get_interface_method_index (method, interface)); + interface = build_class_ref (interface); + } + lookup_arg = tree_cons (NULL_TREE, dtable, + tree_cons (NULL_TREE, interface, + build_tree_list (NULL_TREE, idx))); + return build3 (CALL_EXPR, ptr_type_node, build_address_of (soft_lookupinterfacemethod_node), lookup_arg, NULL_TREE); } /* Expand one of the invoke_* opcodes. - OCPODE is the specific opcode. + OPCODE is the specific opcode. METHOD_REF_INDEX is an index into the constant pool. NARGS is the number of arguments, or -1 if not specified. */ @@ -2048,7 +2265,8 @@ expand_invoke (int opcode, int method_ref_index, int nargs ATTRIBUTE_UNUSED) { tree method_signature = COMPONENT_REF_SIGNATURE(¤t_jcf->cpool, method_ref_index); - tree method_name = COMPONENT_REF_NAME (¤t_jcf->cpool, method_ref_index); + tree method_name = COMPONENT_REF_NAME (¤t_jcf->cpool, + method_ref_index); tree self_type = get_class_constant (current_jcf, COMPONENT_REF_CLASS_INDEX(¤t_jcf->cpool, @@ -2071,38 +2289,82 @@ expand_invoke (int opcode, int method_ref_index, int nargs ATTRIBUTE_UNUSED) method = lookup_java_constructor (self_type, method_signature); else method = lookup_java_method (self_type, method_name, method_signature); + + /* We've found a method in an interface, but this isn't an interface + call. */ + if (opcode != OPCODE_invokeinterface + && method + && (CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (method))))) + method = NULL_TREE; + + /* We've found a non-interface method but we are making an + interface call. This can happen if the interface overrides a + method in Object. */ + if (! flag_verify_invocations + && opcode == OPCODE_invokeinterface + && method + && ! CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (method)))) + method = NULL_TREE; + if (method == NULL_TREE) { - error ("class '%s' has no method named '%s' matching signature '%s'", - self_name, - IDENTIFIER_POINTER (method_name), - IDENTIFIER_POINTER (method_signature)); - } - /* Invoke static can't invoke static/abstract method */ - else if (opcode == OPCODE_invokestatic) - { - if (!METHOD_STATIC (method)) + if (flag_verify_invocations || ! flag_indirect_dispatch) { - error ("invokestatic on non static method"); - method = NULL_TREE; + error ("class '%s' has no method named '%s' matching signature '%s'", + self_name, + IDENTIFIER_POINTER (method_name), + IDENTIFIER_POINTER (method_signature)); } - else if (METHOD_ABSTRACT (method)) + else { - error ("invokestatic on abstract method"); - method = NULL_TREE; + int flags = ACC_PUBLIC; + if (opcode == OPCODE_invokestatic) + flags |= ACC_STATIC; + if (opcode == OPCODE_invokeinterface) + { + flags |= ACC_INTERFACE | ACC_ABSTRACT; + CLASS_INTERFACE (TYPE_NAME (self_type)) = 1; + } + method = add_method (self_type, flags, method_name, + method_signature); + DECL_ARTIFICIAL (method) = 1; + METHOD_DUMMY (method) = 1; + layout_class_method (self_type, NULL, + method, NULL); } } - else + + /* Invoke static can't invoke static/abstract method */ + if (method != NULL_TREE) { - if (METHOD_STATIC (method)) + if (opcode == OPCODE_invokestatic) { - error ("invoke[non-static] on static method"); - method = NULL_TREE; + if (!METHOD_STATIC (method)) + { + error ("invokestatic on non static method"); + method = NULL_TREE; + } + else if (METHOD_ABSTRACT (method)) + { + error ("invokestatic on abstract method"); + method = NULL_TREE; + } + } + else + { + if (METHOD_STATIC (method)) + { + error ("invoke[non-static] on static method"); + method = NULL_TREE; + } } } if (method == NULL_TREE) { + /* If we got here, we emitted an error message above. So we + just pop the arguments, push a properly-typed zero, and + continue. */ method_type = get_type_from_signature (method_signature); pop_arguments (TYPE_ARG_TYPES (method_type)); if (opcode != OPCODE_invokestatic) @@ -2385,6 +2647,7 @@ expand_java_field_op (int is_static, int is_putting, int field_ref_index) tree new_value = is_putting ? pop_value (field_type) : NULL_TREE; tree field_ref; int is_error = 0; + tree original_self_type = self_type; tree field_decl; if (! CLASS_LOADED_P (self_type)) @@ -2396,9 +2659,23 @@ expand_java_field_op (int is_static, int is_putting, int field_ref_index) } else if (field_decl == NULL_TREE) { - error ("missing field '%s' in '%s'", - IDENTIFIER_POINTER (field_name), self_name); - is_error = 1; + if (! flag_verify_invocations) + { + int flags = ACC_PUBLIC; + if (is_static) + flags |= ACC_STATIC; + self_type = original_self_type; + field_decl = add_field (original_self_type, field_name, + field_type, flags); + DECL_ARTIFICIAL (field_decl) = 1; + DECL_IGNORED_P (field_decl) = 1; + } + else + { + error ("missing field '%s' in '%s'", + IDENTIFIER_POINTER (field_name), self_name); + is_error = 1; + } } else if (build_java_signature (TREE_TYPE (field_decl)) != field_signature) { @@ -2637,8 +2914,16 @@ expand_byte_code (JCF *jcf, tree method) } } - if (! verify_jvm_instructions (jcf, byte_ops, length)) - return; + if (flag_new_verifier) + { + if (! verify_jvm_instructions_new (jcf, byte_ops, length)) + return; + } + else + { + if (! verify_jvm_instructions (jcf, byte_ops, length)) + return; + } /* Translate bytecodes. */ linenumber_pointer = linenumber_table; @@ -2749,8 +3034,14 @@ process_jvm_instruction (int PC, const unsigned char* byte_ops, replace the top of the stack with the thrown object reference */ if (instruction_bits [PC] & BCODE_EXCEPTION_TARGET) { - tree type = pop_type (ptr_type_node); - push_value (build_exception_object_ref (type)); + /* Note that the new verifier will not emit a type map at all + for dead exception handlers. In this case we just ignore + the situation. */ + if (! flag_new_verifier || (instruction_bits[PC] & BCODE_VERIFIED) != 0) + { + tree type = pop_type (promote_type (throwable_type_node)); + push_value (build_exception_object_ref (type)); + } } switch (byte_ops[PC++]) diff --git a/gcc/java/gcj.texi b/gcc/java/gcj.texi index 1ef0bab..370585a 100644 --- a/gcc/java/gcj.texi +++ b/gcc/java/gcj.texi @@ -873,7 +873,7 @@ gij [@option{-jar}] [@option{OPTION}] @dots{} @var{CLASS} [@var{ARGS}@dots{}] [@option{-cp} @var{path}] [@option{-classpath} @var{path}] [@option{-D}@var{name}[=@var{value}]@dots{}] [@option{-ms=}@var{number}] [@option{-mx=}@var{number}] - [@option{-X@var{argument}] + [@option{-X@var{argument}}] [@option{-verbose}] [@option{-verbose:class}] [@option{--showversion}] [@option{--version}] [@option{--help}][@option{-?}] @c man end @c man begin SEEALSO gij @@ -954,7 +954,8 @@ Print version number and continue. @item --version Print version number, then exit. -@item -verbose:class +@item -verbose +@itemx -verbose:class Each time a class is initialized, print a short message on standard error. @end table @@ -2385,6 +2386,24 @@ property is set to @samp{cache}, then any failed lookups are cached and not tried again. If this property is set to @samp{never}, then lookups are never done. For more information, @xref{Extensions}. +@item gnu.gcj.jit.compiler +@c FIXME we should probably have a whole node on this... +This is the full path to @command{gcj} executable which should be +used to compile classes just-in-time when +@code{ClassLoader.defineClass} is called. If not set, @command{gcj} +will not be invoked by the runtime; this can also be controlled via +@code{Compiler.disable}. + +@item gnu.gcj.jit.options +This is a space-separated string of options which should be passed to +@command{gcj} when in JIT mode. If not set, a sensible default is +chosen. + +@item gnu.gcj.jit.cachedir +This is the directory where cached shared library files are +stored. If not set, JIT compilation is disabled. This should never +be set to a directory that is writable by any other user. + @end table diff --git a/gcc/java/gjavah.c b/gcc/java/gjavah.c index abf75c4..0badaf1 100644 --- a/gcc/java/gjavah.c +++ b/gcc/java/gjavah.c @@ -112,6 +112,9 @@ static JCF_u2 last_access; #define METHOD_IS_NATIVE(Method) \ ((Method) & ACC_NATIVE) +#define METHOD_IS_PRIVATE(Class, Method) \ + (((Method) & ACC_PRIVATE) != 0) + /* We keep a linked list of all method names we have seen. This lets us determine if a method name and a field name are in conflict. */ struct method_name @@ -937,7 +940,7 @@ print_method_info (FILE *stream, JCF* jcf, int name_index, int sig_index, fputs (" ", out); if ((flags & ACC_STATIC)) fputs ("static ", out); - else if (! METHOD_IS_FINAL (jcf->access_flags, flags)) + else if (! METHOD_IS_PRIVATE (jcf->access_flags, flags)) { /* Don't print `virtual' if we have a constructor. */ if (! is_init) diff --git a/gcc/java/java-except.h b/gcc/java/java-except.h index 3aa5e01..45e4f0a 100644 --- a/gcc/java/java-except.h +++ b/gcc/java/java-except.h @@ -53,6 +53,8 @@ struct eh_range /* The TRY_CATCH_EXPR for this EH range. */ tree stmt; + + tree handler; }; /* A dummy range that represents the entire method. */ diff --git a/gcc/java/java-tree.h b/gcc/java/java-tree.h index 558d0f5..e8144ee 100644 --- a/gcc/java/java-tree.h +++ b/gcc/java/java-tree.h @@ -227,6 +227,9 @@ extern int flag_indirect_dispatch; /* When zero, don't generate runtime array store checks. */ extern int flag_store_check; +/* When nonzero, use the new bytecode verifier. */ +extern int flag_new_verifier; + /* Encoding used for source files. */ extern const char *current_encoding; @@ -237,6 +240,8 @@ extern GTY(()) struct JCF * current_jcf; before static field references. */ extern int always_initialize_class_p; +extern int flag_verify_invocations; + typedef struct CPool constant_pool; #define CONSTANT_ResolvedFlag 16 @@ -367,9 +372,13 @@ enum java_tree_index JTI_OTABLE_PTR_TYPE, JTI_ATABLE_TYPE, JTI_ATABLE_PTR_TYPE, + JTI_ITABLE_TYPE, + JTI_ITABLE_PTR_TYPE, JTI_SYMBOL_TYPE, JTI_SYMBOLS_ARRAY_TYPE, JTI_SYMBOLS_ARRAY_PTR_TYPE, + JTI_ASSERTION_ENTRY_TYPE, + JTI_ASSERTION_TABLE_TYPE, JTI_END_PARAMS_NODE, @@ -388,6 +397,7 @@ enum java_tree_index JTI_SOFT_MONITORENTER_NODE, JTI_SOFT_MONITOREXIT_NODE, JTI_SOFT_LOOKUPINTERFACEMETHOD_NODE, + JTI_SOFT_LOOKUPINTERFACEMETHODBYNAME_NODE, JTI_SOFT_LOOKUPJNIMETHOD_NODE, JTI_SOFT_GETJNIENVNEWFRAME_NODE, JTI_SOFT_JNIPOPSYSTEMFRAME_NODE, @@ -597,18 +607,24 @@ extern GTY(()) tree java_global_trees[JTI_MAX]; java_global_trees[JTI_OTABLE_TYPE] #define atable_type \ java_global_trees[JTI_ATABLE_TYPE] +#define itable_type \ + java_global_trees[JTI_ITABLE_TYPE] #define otable_ptr_type \ java_global_trees[JTI_OTABLE_PTR_TYPE] #define atable_ptr_type \ java_global_trees[JTI_ATABLE_PTR_TYPE] +#define itable_ptr_type \ + java_global_trees[JTI_ITABLE_PTR_TYPE] #define symbol_type \ java_global_trees[JTI_SYMBOL_TYPE] #define symbols_array_type \ java_global_trees[JTI_SYMBOLS_ARRAY_TYPE] #define symbols_array_ptr_type \ - java_global_trees[JTI_SYMBOLS_ARRAY_PTR_TYPE] -#define class_refs_decl \ - Jjava_global_trees[TI_CLASS_REFS_DECL] + java_global_trees[JTI_SYMBOLS_ARRAY_PTR_TYPE] +#define assertion_entry_type \ + java_global_trees[JTI_ASSERTION_ENTRY_TYPE] +#define assertion_table_type \ + java_global_trees[JTI_ASSERTION_TABLE_TYPE] #define end_params_node \ java_global_trees[JTI_END_PARAMS_NODE] @@ -644,6 +660,8 @@ extern GTY(()) tree java_global_trees[JTI_MAX]; java_global_trees[JTI_SOFT_MONITOREXIT_NODE] #define soft_lookupinterfacemethod_node \ java_global_trees[JTI_SOFT_LOOKUPINTERFACEMETHOD_NODE] +#define soft_lookupinterfacemethodbyname_node \ + java_global_trees[JTI_SOFT_LOOKUPINTERFACEMETHODBYNAME_NODE] #define soft_lookupjnimethod_node \ java_global_trees[JTI_SOFT_LOOKUPJNIMETHOD_NODE] #define soft_getjnienvnewframe_node \ @@ -984,6 +1002,7 @@ struct lang_decl_func GTY(()) unsigned int invisible : 1; /* Set for methods we generate internally but which shouldn't be written to the .class file. */ + unsigned int dummy:1; }; struct treetreehash_entry GTY(()) @@ -992,6 +1011,22 @@ struct treetreehash_entry GTY(()) tree value; }; +/* These represent the possible assertion_code's that can be emitted in the + type assertion table. */ +enum +{ + JV_ASSERT_END_OF_TABLE = 0, /* Last entry in table. */ + JV_ASSERT_TYPES_COMPATIBLE = 1, /* Operand A is assignable to Operand B. */ + JV_ASSERT_IS_INSTANTIABLE = 2 /* Operand A is an instantiable class. */ +}; + +typedef struct type_assertion GTY(()) +{ + int assertion_code; /* 'opcode' for the type of this assertion. */ + tree op1; /* First operand. */ + tree op2; /* Second operand. */ +} type_assertion; + extern tree java_treetreehash_find (htab_t, tree); extern tree * java_treetreehash_new (htab_t, tree); extern htab_t java_treetreehash_create (size_t size, int ggc); @@ -1043,6 +1078,9 @@ struct lang_decl GTY(()) #define TYPE_II_STMT_LIST(T) (TYPE_LANG_SPECIFIC (T)->ii_block) /* The decl of the synthetic method `class$' used to handle `.class' for non primitive types when compiling to bytecode. */ + +#define TYPE_DUMMY(T) (TYPE_LANG_SPECIFIC(T)->dummy_class) + #define TYPE_DOT_CLASS(T) (TYPE_LANG_SPECIFIC (T)->dot_class) #define TYPE_PACKAGE_LIST(T) (TYPE_LANG_SPECIFIC (T)->package_list) #define TYPE_IMPORT_LIST(T) (TYPE_LANG_SPECIFIC (T)->import_list) @@ -1060,10 +1098,16 @@ struct lang_decl GTY(()) #define TYPE_OTABLE_SYMS_DECL(T) (TYPE_LANG_SPECIFIC (T)->otable_syms_decl) #define TYPE_OTABLE_DECL(T) (TYPE_LANG_SPECIFIC (T)->otable_decl) +#define TYPE_ITABLE_METHODS(T) (TYPE_LANG_SPECIFIC (T)->itable_methods) +#define TYPE_ITABLE_SYMS_DECL(T) (TYPE_LANG_SPECIFIC (T)->itable_syms_decl) +#define TYPE_ITABLE_DECL(T) (TYPE_LANG_SPECIFIC (T)->itable_decl) + #define TYPE_CTABLE_DECL(T) (TYPE_LANG_SPECIFIC (T)->ctable_decl) #define TYPE_CATCH_CLASSES(T) (TYPE_LANG_SPECIFIC (T)->catch_classes) +#define TYPE_VERIFY_METHOD(T) (TYPE_LANG_SPECIFIC (T)->verify_method) #define TYPE_TO_RUNTIME_MAP(T) (TYPE_LANG_SPECIFIC (T)->type_to_runtime_map) +#define TYPE_ASSERTIONS(T) (TYPE_LANG_SPECIFIC (T)->type_assertions) struct lang_type GTY(()) { @@ -1092,18 +1136,31 @@ struct lang_type GTY(()) tree atable_decl; /* The static address table. */ tree atable_syms_decl; + tree itable_methods; /* List of interfaces methods referred + to by this class. */ + tree itable_decl; /* The interfaces table. */ + tree itable_syms_decl; + tree ctable_decl; /* The table of classes for the runtime type matcher. */ tree catch_classes; + tree verify_method; /* The verify method for this class. + Used in split verification. */ + htab_t GTY ((param_is (struct treetreehash_entry))) type_to_runtime_map; /* The mapping of classes to exception region markers. */ + htab_t GTY ((param_is (struct type_assertion))) type_assertions; + /* Table of type assertions to be evaluated + by the runtime when this class is loaded. */ + unsigned pic:1; /* Private Inner Class. */ unsigned poic:1; /* Protected Inner Class. */ unsigned strictfp:1; /* `strictfp' class. */ unsigned assertions:1; /* Any method uses `assert'. */ + unsigned dummy_class:1; /* Not a real class, just a placeholder. */ }; #define JCF_u4 unsigned long @@ -1243,7 +1300,7 @@ extern void make_class_data (tree); extern void register_class (void); extern int alloc_name_constant (int, tree); extern void emit_register_classes (tree *); -extern tree emit_symbol_table (tree, tree, tree, tree, tree); +extern tree emit_symbol_table (tree, tree, tree, tree, tree, int); extern void lang_init_source (int); extern void write_classfile (tree); extern char *print_int_node (tree); @@ -1259,9 +1316,12 @@ extern int alloc_class_constant (tree); extern void init_expr_processing (void); extern void push_super_field (tree, tree); extern void init_class_processing (void); +extern void add_type_assertion (tree, int, tree, tree); extern int can_widen_reference_to (tree, tree); extern int class_depth (tree); extern int verify_jvm_instructions (struct JCF *, const unsigned char *, long); +extern int verify_jvm_instructions_new (struct JCF *, const unsigned char *, + long); extern void maybe_pushlevels (int); extern void maybe_poplevels (int); extern void force_poplevels (int); @@ -1274,6 +1334,7 @@ extern void push_type (tree); extern void load_type_state (tree); extern void add_interface (tree, tree); extern tree force_evaluation_order (tree); +extern tree java_create_object (tree); extern int verify_constant_pool (struct JCF *); extern void start_java_method (tree); extern void end_java_method (void); @@ -1321,7 +1382,6 @@ extern tree java_mangle_decl (struct obstack *, tree); extern tree java_mangle_class_field (struct obstack *, tree); extern tree java_mangle_class_field_from_string (struct obstack *, char *); extern tree java_mangle_vtable (struct obstack *, tree); -extern const char *lang_printable_name_wls (tree, int); extern void append_gpp_mangled_name (const char *, int); extern void add_predefined_file (tree); @@ -1363,6 +1423,8 @@ extern tree builtin_function (const char *, tree, int, enum built_in_class, /* Access flags etc for a method (a FUNCTION_DECL): */ +#define METHOD_DUMMY(DECL) (DECL_LANG_SPECIFIC (DECL)->u.f.dummy) + #define METHOD_PUBLIC(DECL) DECL_LANG_FLAG_1 (FUNCTION_DECL_CHECK (DECL)) #define METHOD_PRIVATE(DECL) TREE_PRIVATE (FUNCTION_DECL_CHECK (DECL)) #define METHOD_PROTECTED(DECL) TREE_PROTECTED (FUNCTION_DECL_CHECK (DECL)) @@ -1800,7 +1862,8 @@ enum JV_STATE_PRELOADING = 1, /* Can do _Jv_FindClass. */ JV_STATE_LOADING = 3, /* Has super installed. */ - JV_STATE_LOADED = 5, /* Is complete. */ + JV_STATE_READ = 4, /* Has been completely defined. */ + JV_STATE_LOADED = 5, /* Has Miranda methods defined. */ JV_STATE_COMPILED = 6, /* This was a compiled class. */ diff --git a/gcc/java/jcf-parse.c b/gcc/java/jcf-parse.c index f04b9f9..a7cd57a 100644 --- a/gcc/java/jcf-parse.c +++ b/gcc/java/jcf-parse.c @@ -610,8 +610,14 @@ void load_class (tree class_or_name, int verbose) { tree name, saved; - int class_loaded; - tree class_decl; + int class_loaded = 0; + tree class_decl = NULL_TREE; + bool is_compiled_class = false; + + /* We've already failed, don't try again. */ + if (TREE_CODE (class_or_name) == RECORD_TYPE + && TYPE_DUMMY (class_or_name)) + return; /* class_or_name can be the name of the class we want to load */ if (TREE_CODE (class_or_name) == IDENTIFIER_NODE) @@ -624,41 +630,99 @@ load_class (tree class_or_name, int verbose) else name = DECL_NAME (TYPE_NAME (class_or_name)); + class_decl = IDENTIFIER_CLASS_VALUE (name); + if (class_decl != NULL_TREE) + { + tree type = TREE_TYPE (class_decl); + is_compiled_class + = ((TYPE_JCF (type) && JCF_SEEN_IN_ZIP (TYPE_JCF (type))) + || CLASS_FROM_CURRENTLY_COMPILED_P (type)); + } + /* If the class is from source code, then it must already be loaded. */ class_decl = IDENTIFIER_CLASS_VALUE (name); if (class_decl && CLASS_FROM_SOURCE_P (TREE_TYPE (class_decl))) return; saved = name; - while (1) + + /* If flag_verify_invocations is unset, we don't try to load a class + unless we're looking for Object (which is fixed by the ABI) or + it's a class that we're going to compile. */ + if (flag_verify_invocations + || class_or_name == object_type_node + || is_compiled_class + || TREE_CODE (class_or_name) == IDENTIFIER_NODE) { - char *separator; + while (1) + { + char *separator; - if ((class_loaded = read_class (name))) - break; + /* We've already loaded it. */ + if (IDENTIFIER_CLASS_VALUE (name) != NULL_TREE) + { + tree tmp_decl = IDENTIFIER_CLASS_VALUE (name); + if (CLASS_PARSED_P (TREE_TYPE (tmp_decl))) + break; + } + + if (read_class (name)) + break; - /* We failed loading name. Now consider that we might be looking - for a inner class. */ - if ((separator = strrchr (IDENTIFIER_POINTER (name), '$')) - || (separator = strrchr (IDENTIFIER_POINTER (name), '.'))) - { - int c = *separator; - *separator = '\0'; - name = get_identifier (IDENTIFIER_POINTER (name)); - *separator = c; - - /* Otherwise we might get infinite recursion, if say we have - String.class but not String$CaseInsensitiveComparator.class. */ - if (current_jcf && current_jcf->java_source == 0) + /* We failed loading name. Now consider that we might be looking + for a inner class. */ + if ((separator = strrchr (IDENTIFIER_POINTER (name), '$')) + || (separator = strrchr (IDENTIFIER_POINTER (name), '.'))) + { + int c = *separator; + *separator = '\0'; + name = get_identifier (IDENTIFIER_POINTER (name)); + *separator = c; + + /* Otherwise we might get infinite recursion, if say we + have String.class but not + String$CaseInsensitiveComparator.class. */ + if (current_jcf && current_jcf->java_source == 0) + break; + } + /* Otherwise, we failed, we bail. */ + else break; } - /* Otherwise, we failed, we bail. */ - else - break; - } - if (!class_loaded && verbose) - error ("cannot find file for class %s", IDENTIFIER_POINTER (saved)); + { + /* have we found the class we're looking for? */ + tree type_decl = IDENTIFIER_CLASS_VALUE (saved); + tree type = type_decl ? TREE_TYPE (type_decl) : NULL; + class_loaded = type && CLASS_PARSED_P (type); + } + } + + if (!class_loaded) + { + if (flag_verify_invocations || ! flag_indirect_dispatch + || flag_emit_class_files) + { + if (verbose) + error ("cannot find file for class %s", IDENTIFIER_POINTER (saved)); + } + else if (verbose) + { + /* This is just a diagnostic during testing, not a real problem. */ + if (!quiet_flag) + warning("cannot find file for class %s", + IDENTIFIER_POINTER (saved)); + + /* Fake it. */ + if (TREE_CODE (class_or_name) == RECORD_TYPE) + { + set_super_info (0, class_or_name, object_type_node, 0); + TYPE_DUMMY (class_or_name) = 1; + /* We won't be able to output any debug info for this class. */ + DECL_IGNORED_P (TYPE_NAME (class_or_name)) = 1; + } + } + } } /* Parse the .class file JCF. */ @@ -760,6 +824,7 @@ parse_class_file (void) java_layout_seen_class_methods (); input_location = DECL_SOURCE_LOCATION (TYPE_NAME (current_class)); + file_start_location = input_location; (*debug_hooks->start_source_file) (input_line, input_filename); /* Currently we always have to emit calls to _Jv_InitClass when @@ -775,7 +840,7 @@ parse_class_file (void) { JCF *jcf = current_jcf; - if (METHOD_ABSTRACT (method)) + if (METHOD_ABSTRACT (method) || METHOD_DUMMY (method)) continue; if (METHOD_NATIVE (method)) @@ -911,6 +976,7 @@ static void parse_source_file_2 (void) { int save_error_count = java_error_count; + flag_verify_invocations = true; java_complete_class (); /* Parse unsatisfied class decl. */ java_parse_abort_on_error (); } @@ -1196,7 +1262,12 @@ java_parse_file (int set_yydebug ATTRIBUTE_UNUSED) input_location = DECL_SOURCE_LOCATION (node); if (CLASS_FILE_P (node)) { + /* FIXME: These two flags really should be independent. We + should be able to compile fully binary compatible, but + with flag_verify_invocations on. */ + flag_verify_invocations = ! flag_indirect_dispatch; output_class = current_class = TREE_TYPE (node); + current_jcf = TYPE_JCF (current_class); layout_class (current_class); load_inner_classes (current_class); @@ -1232,13 +1303,15 @@ compute_class_name (struct ZipDirectory *zdir) char *class_name_in_zip_dir = ZIPDIR_FILENAME (zdir); char *class_name; int i; - int filename_length; + int filename_length = zdir->filename_length; - while (strncmp (class_name_in_zip_dir, "./", 2) == 0) - class_name_in_zip_dir += 2; + while (filename_length > 2 && strncmp (class_name_in_zip_dir, "./", 2) == 0) + { + class_name_in_zip_dir += 2; + filename_length -= 2; + } - filename_length = (strlen (class_name_in_zip_dir) - - strlen (".class")); + filename_length -= strlen (".class"); class_name = ALLOC (filename_length + 1); memcpy (class_name, class_name_in_zip_dir, filename_length); class_name [filename_length] = '\0'; @@ -1300,6 +1373,13 @@ parse_zip_file_entries (void) current_jcf = TYPE_JCF (class); output_class = current_class = class; + if (TYPE_DUMMY (class)) + { + /* This is a dummy class, and now we're compiling it + for real. */ + abort (); + } + /* This is for a corner case where we have a superclass but no superclass fields. diff --git a/gcc/java/jvspec.c b/gcc/java/jvspec.c index 940cd8c9..a3e8c6f 100644 --- a/gcc/java/jvspec.c +++ b/gcc/java/jvspec.c @@ -68,7 +68,7 @@ static const char jvgenmain_spec[] = %<fcompile-resource* %<fassert %<fno-assert \ %<femit-class-file %<femit-class-files %<fencoding*\ %<fuse-boehm-gc %<fhash-synchronization %<fjni\ - %<findirect-dispatch \ + %<findirect-dispatch %<fnew-verifier\ %<fno-store-check %<foutput-class-dir\ %<fclasspath* %<fCLASSPATH* %<fbootclasspath*\ %<fextdirs*\ diff --git a/gcc/java/lang.c b/gcc/java/lang.c index be789fe..bcf4e45 100644 --- a/gcc/java/lang.c +++ b/gcc/java/lang.c @@ -128,6 +128,17 @@ int flag_wall = 0; /* The encoding of the source file. */ const char *current_encoding = NULL; +/* When nonzero, report use of deprecated classes, methods, or fields. */ +int flag_deprecated = 1; + +/* When zero, don't optimize static class initialization. This flag shouldn't + be tested alone, use STATIC_CLASS_INITIALIZATION_OPTIMIZATION_P instead. */ +/* FIXME: Make this work with gimplify. */ +/* int flag_optimize_sci = 0; */ + +/* Don't attempt to verify invocations. */ +int flag_verify_invocations = 0; + /* When nonzero, print extra version information. */ static int v_flag = 0; @@ -593,6 +604,11 @@ java_post_options (const char **pfilename) if (flag_inline_functions) flag_inline_trees = 2; + /* An absolute requirement: if we're not using indirect dispatch, we + must always verify everything. */ + if (! flag_indirect_dispatch) + flag_verify_invocations = true; + /* Open input file. */ if (filename == 0 || !strcmp (filename, "-")) @@ -972,6 +988,10 @@ java_get_callee_fndecl (tree call_expr) HOST_WIDE_INT index; + /* FIXME: This is disabled because we end up passing calls through + the PLT, and we do NOT want to do that. */ + return NULL; + if (TREE_CODE (call_expr) != CALL_EXPR) return NULL; method = TREE_OPERAND (call_expr, 0); diff --git a/gcc/java/lang.opt b/gcc/java/lang.opt index 0e4ffcf..644bd5b 100644 --- a/gcc/java/lang.opt +++ b/gcc/java/lang.opt @@ -176,5 +176,9 @@ fuse-divide-subroutine Java Var(flag_use_divide_subroutine) Init(1) Call a library routine to do integer divisions +fnew-verifier +Java Var(flag_new_verifier) +Enable the new bytecode verifier + version Java diff --git a/gcc/java/parse.y b/gcc/java/parse.y index 1c1a686..91776c6 100644 --- a/gcc/java/parse.y +++ b/gcc/java/parse.y @@ -9690,8 +9690,8 @@ strip_out_static_field_access_decl (tree node) tree call = TREE_OPERAND (op1, 0); if (TREE_CODE (call) == CALL_EXPR && TREE_CODE (TREE_OPERAND (call, 0)) == ADDR_EXPR - && TREE_OPERAND (TREE_OPERAND (call, 0), 0) - == soft_initclass_node) + && (TREE_OPERAND (TREE_OPERAND (call, 0), 0) + == soft_initclass_node)) return TREE_OPERAND (op1, 1); } else if (JDECL_P (op1)) @@ -11025,7 +11025,7 @@ patch_invoke (tree patch, tree method, tree args) if (TREE_CODE (original_call) == NEW_CLASS_EXPR) { tree class = DECL_CONTEXT (method); - tree c1, saved_new, size, new; + tree c1, saved_new, new; tree alloc_node; if (flag_emit_class_files || flag_emit_xref) @@ -11035,7 +11035,6 @@ patch_invoke (tree patch, tree method, tree args) } if (!TYPE_SIZE (class)) safe_layout_class (class); - size = size_in_bytes (class); alloc_node = (class_has_finalize_method (class) ? alloc_object_node : alloc_no_finalizer_node); @@ -11109,11 +11108,20 @@ invocation_mode (tree method, int super) if (DECL_CONSTRUCTOR_P (method)) return INVOKE_STATIC; - if (access & ACC_FINAL || access & ACC_PRIVATE) + if (access & ACC_PRIVATE) return INVOKE_NONVIRTUAL; - if (CLASS_FINAL (TYPE_NAME (DECL_CONTEXT (method)))) - return INVOKE_NONVIRTUAL; + /* Binary compatibility: just because it's final today, that doesn't + mean it'll be final tomorrow. */ + if (! flag_indirect_dispatch + || DECL_CONTEXT (method) == object_type_node) + { + if (access & ACC_FINAL) + return INVOKE_NONVIRTUAL; + + if (CLASS_FINAL (TYPE_NAME (DECL_CONTEXT (method)))) + return INVOKE_NONVIRTUAL; + } if (CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (method)))) return INVOKE_INTERFACE; diff --git a/gcc/java/typeck.c b/gcc/java/typeck.c index 0c61174..4db69b4 100644 --- a/gcc/java/typeck.c +++ b/gcc/java/typeck.c @@ -743,7 +743,7 @@ lookup_java_method (tree searched_class, tree method_name, method_signature, build_java_signature); } -/* Return true iff CLASS (or its ancestors) has a method METHOD_NAME. */ +/* Return true iff CLASS (or its ancestors) has a method METHOD_NAME. */ int has_method (tree class, tree method_name) { diff --git a/gcc/java/verify-glue.c b/gcc/java/verify-glue.c new file mode 100644 index 0000000..fb21340 --- /dev/null +++ b/gcc/java/verify-glue.c @@ -0,0 +1,514 @@ +/* Glue to interface gcj with bytecode verifier. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +Java and all Java-based marks are trademarks or registered trademarks +of Sun Microsystems, Inc. in the United States and other countries. +The Free Software Foundation is independent of Sun Microsystems, Inc. */ + +/* Written by Tom Tromey <tromey@redhat.com>. */ + +#include "config.h" + +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "errors.h" +#include "parse.h" + +#include "verify.h" +#include "java-tree.h" +#include "java-except.h" + +void * +vfy_alloc (size_t bytes) +{ + return xmalloc (bytes); +} + +void +vfy_free (void *mem) +{ + free (mem); +} + +bool +vfy_strings_equal (vfy_string one, vfy_string two) +{ + return one == two; +} + +const char * +vfy_string_bytes (vfy_string str) +{ + return IDENTIFIER_POINTER (str); +} + +int +vfy_string_length (vfy_string str) +{ + return IDENTIFIER_LENGTH (str); +} + +vfy_string +vfy_init_name () +{ + return init_identifier_node; +} + +vfy_string +vfy_clinit_name () +{ + return clinit_identifier_node; +} + +static const char* +skip_one_type (const char* ptr) +{ + int ch = *ptr++; + + while (ch == '[') + { + ch = *ptr++; + } + + if (ch == 'L') + { + do { ch = *ptr++; } while (ch != ';'); + } + + return ptr; +} + +int +vfy_count_arguments (vfy_string signature) +{ + const char *ptr = IDENTIFIER_POINTER (signature); + int arg_count = 0; + + /* Skip '('. */ + ptr++; + + /* Count args. */ + while (*ptr != ')') + { + ptr = skip_one_type (ptr); + arg_count += 1; + } + + return arg_count; +} + +vfy_string +vfy_get_string (const char *s, int len) +{ + return get_identifier_with_length (s, len); +} + +vfy_string +vfy_get_signature (vfy_method *method) +{ + return method->signature; +} + +vfy_string +vfy_get_method_name (vfy_method *method) +{ + return method->name; +} + +bool +vfy_is_static (vfy_method *method) +{ + return METHOD_STATIC (method->method); +} + +const unsigned char * +vfy_get_bytecode (vfy_method *method) +{ + return method->bytes; +} + +vfy_exception * +vfy_get_exceptions (vfy_method *method) +{ + return method->exceptions; +} + +void +vfy_get_exception (vfy_exception *exceptions, int index, int *handler, + int *start, int *end, int *handler_type) +{ + *handler = exceptions[index].handler; + *start = exceptions[index].start; + *end = exceptions[index].end; + *handler_type = exceptions[index].type; +} + +int +vfy_tag (vfy_constants *pool, int index) +{ + int result = JPOOL_TAG (pool, index); + /* gcj will resolve constant pool entries other than string and + class references. The verifier doesn't care about the values, so + we just strip off the resolved flag. */ + if ((result & CONSTANT_ResolvedFlag) != 0 + && result != CONSTANT_ResolvedString + && result != CONSTANT_ResolvedClass) + result &= ~ CONSTANT_ResolvedFlag; + return result; +} + +void +vfy_load_indexes (vfy_constants *pool, int index, + vfy_uint_16 *index0, vfy_uint_16 *index1) +{ + *index0 = JPOOL_USHORT1 (pool, index); + *index1 = JPOOL_USHORT2 (pool, index); +} + +vfy_constants * +vfy_get_constants (vfy_jclass klass) +{ + return TYPE_JCF (klass); +} + +int +vfy_get_constants_size (vfy_jclass klass) +{ + return JPOOL_SIZE (TYPE_JCF (klass)); +} + +vfy_string +vfy_get_pool_string (vfy_constants *pool, int index) +{ + return get_name_constant (pool, index); +} + +vfy_jclass +vfy_get_pool_class (vfy_constants *pool, int index) +{ + vfy_jclass k; + k = get_class_constant (pool, index); + return k; +} + +vfy_string +vfy_make_string (const char *s, int len) +{ + tree result; + char *s2 = (char *) s; + char save = s2[len]; + s2[len] = '\0'; + result = get_identifier (s2); + s2[len] = save; + return result; +} + +vfy_string +vfy_get_class_name (vfy_jclass klass) +{ + return DECL_NAME (TYPE_NAME (klass)); +} + +bool +vfy_is_assignable_from (vfy_jclass target, vfy_jclass source) +{ + /* At compile time, for the BC-ABI we assume that reference types are always + compatible. However, a type assertion table entry is emitted so that the + runtime can detect binary-incompatible changes. */ + + /* FIXME: implement real test for old ABI. */ + + /* Any class is always assignable to itself, or java.lang.Object. */ + if (source == target || target == object_type_node) + return true; + + /* Otherwise, a type assertion is required. */ + add_type_assertion (current_class, JV_ASSERT_TYPES_COMPATIBLE, source, + target); + return true; +} + +char +vfy_get_primitive_char (vfy_jclass klass) +{ + tree sig; + if (! vfy_is_primitive (klass)) + abort (); + sig = build_java_signature (klass); + return (IDENTIFIER_POINTER (sig))[0]; +} + +int +vfy_get_interface_count (vfy_jclass klass ATTRIBUTE_UNUSED) +{ + /* FIXME: Need to merge from mainline to get this. */ + #if 0 + return BINFO_N_BASE_BINFOS (klass); + #endif + return -1; +} + +vfy_jclass +vfy_get_interface (vfy_jclass klass ATTRIBUTE_UNUSED, int index ATTRIBUTE_UNUSED) +{ + /* FIXME: Need to merge from mainline to get this. */ + #if 0 + vfy_jclass k; + k = BINFO_BASE_BINFO (klass, index); + return k; + #endif + return NULL; +} + +bool +vfy_is_array (vfy_jclass klass) +{ + return TYPE_ARRAY_P (klass); +} + +bool +vfy_is_interface (vfy_jclass klass) +{ + return CLASS_INTERFACE (TYPE_NAME (klass)); +} + +bool +vfy_is_primitive (vfy_jclass klass) +{ + return JPRIMITIVE_TYPE_P (klass); +} + +vfy_jclass +vfy_get_superclass (vfy_jclass klass) +{ + vfy_jclass k; + k = CLASSTYPE_SUPER (klass); + return k; +} + +vfy_jclass +vfy_get_array_class (vfy_jclass klass) +{ + vfy_jclass k; + k = build_java_array_type (klass, -1); + return k; +} + +vfy_jclass +vfy_get_component_type (vfy_jclass klass) +{ + vfy_jclass k; + if (! vfy_is_array (klass)) + abort (); + k = TYPE_ARRAY_ELEMENT (klass); + if (TREE_CODE (k) == POINTER_TYPE) + k = TREE_TYPE (k); + return k; +} + +bool +vfy_is_abstract (vfy_jclass klass) +{ + return CLASS_ABSTRACT (TYPE_NAME (klass)); +} + +vfy_jclass +vfy_find_class (vfy_jclass ignore ATTRIBUTE_UNUSED, vfy_string name) +{ + vfy_jclass k; + + k = get_type_from_signature (name); + if (TREE_CODE (k) == POINTER_TYPE) + k = TREE_TYPE (k); + + return k; +} + +vfy_jclass +vfy_object_type () +{ + vfy_jclass k; + k = object_type_node; + return k; +} + +vfy_jclass +vfy_string_type () +{ + vfy_jclass k; + k = string_type_node; + return k; +} + +vfy_jclass +vfy_throwable_type () +{ + vfy_jclass k; + k = throwable_type_node; + return k; +} + +vfy_jclass +vfy_unsuitable_type (void) +{ + return TYPE_SECOND; +} + +vfy_jclass +vfy_return_address_type (void) +{ + return TYPE_RETURN_ADDR; +} + +vfy_jclass +vfy_null_type (void) +{ + return TYPE_NULL; +} + +int +vfy_fail (const char *message, int pc, vfy_jclass ignore1 ATTRIBUTE_UNUSED, + vfy_method *ignore2 ATTRIBUTE_UNUSED) +{ + if (pc == -1) + error ("verification failed: %s", message); + else + error ("verification failed at PC=%d: %s", pc, message); + /* We have to return a value for the verifier to throw. */ + return 1; +} + +vfy_jclass +vfy_get_primitive_type (int type) +{ + vfy_jclass k; + k = decode_newarray_type (type); + return k; +} + +void +vfy_note_stack_depth (vfy_method *method, int pc, int depth) +{ + tree label = lookup_label (pc); + LABEL_TYPE_STATE (label) = make_tree_vec (method->max_locals + depth); +} + +void +vfy_note_stack_type (vfy_method *method, int pc, int slot, vfy_jclass type) +{ + tree label, vec; + + slot += method->max_locals; + + if (type == object_type_node) + type = object_ptr_type_node; + + label = lookup_label (pc); + vec = LABEL_TYPE_STATE (label); + TREE_VEC_ELT (vec, slot) = type; +} + +void +vfy_note_local_type (vfy_method *method ATTRIBUTE_UNUSED, int pc, int slot, + vfy_jclass type) +{ + tree label, vec; + + if (type == object_type_node) + type = object_ptr_type_node; + + label = lookup_label (pc); + vec = LABEL_TYPE_STATE (label); + TREE_VEC_ELT (vec, slot) = type; +} + +void +vfy_note_instruction_seen (int pc) +{ + instruction_bits[pc] |= BCODE_VERIFIED; +} + +/* Verify the bytecodes of the current method. + Return 1 on success, 0 on failure. */ +int +verify_jvm_instructions_new (JCF *jcf, const unsigned char *byte_ops, + long length) +{ + vfy_method method; + int i, result, eh_count; + vfy_exception *exceptions; + + method_init_exceptions (); + + JCF_SEEK (jcf, DECL_CODE_OFFSET (current_function_decl) + length); + eh_count = JCF_readu2 (jcf); + + exceptions = (vfy_exception *) xmalloc (eh_count * sizeof (vfy_exception)); + for (i = 0; i < eh_count; ++i) + { + int start_pc, end_pc, handler_pc, catch_type; + unsigned char *p = jcf->read_ptr + 8 * i; + start_pc = GET_u2 (p); + end_pc = GET_u2 (p+2); + handler_pc = GET_u2 (p+4); + catch_type = GET_u2 (p+6); + + if (start_pc < 0 || start_pc >= length + || end_pc < 0 || end_pc > length || start_pc >= end_pc + || handler_pc < 0 || handler_pc >= length) + { + error ("bad pc in exception_table"); + free (exceptions); + return 0; + } + + exceptions[i].handler = handler_pc; + exceptions[i].start = start_pc; + exceptions[i].end = end_pc; + exceptions[i].type = catch_type; + + add_handler (start_pc, end_pc, + lookup_label (handler_pc), + catch_type == 0 ? NULL_TREE + : get_class_constant (jcf, catch_type)); + instruction_bits[handler_pc] |= BCODE_EXCEPTION_TARGET; + } + + handle_nested_ranges (); + + method.method = current_function_decl; + method.signature = build_java_signature (TREE_TYPE (current_function_decl)); + method.name = DECL_NAME (current_function_decl); + method.bytes = byte_ops; + method.exceptions = exceptions; + method.defining_class = DECL_CONTEXT (current_function_decl); + method.max_stack = DECL_MAX_STACK (current_function_decl); + method.max_locals = DECL_MAX_LOCALS (current_function_decl); + method.code_length = length; + method.exc_count = eh_count; + + result = verify_method (&method); + + free (exceptions); + + return result; +} diff --git a/gcc/java/verify-impl.c b/gcc/java/verify-impl.c new file mode 100644 index 0000000..2c402a8 --- /dev/null +++ b/gcc/java/verify-impl.c @@ -0,0 +1,3418 @@ +/* Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +/* Written by Tom Tromey <tromey@redhat.com> */ + +/* Uncomment this to enable debugging output. */ +/* #define VERIFY_DEBUG */ + +#include "config.h" + +#include "verify.h" + +/* Hack to work around namespace pollution from java-tree.h. */ +#undef current_class + +#ifdef VERIFY_DEBUG +#include <stdio.h> +#endif /* VERIFY_DEBUG */ + +/* This is used to mark states which are not scheduled for + verification. */ +#define INVALID_STATE ((state *) -1) + +#ifdef VERIFY_DEBUG +static void +debug_print (const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); +} +#else +static void +debug_print (const char *fmt ATTRIBUTE_UNUSED, ...) +{ +} +#endif /* VERIFY_DEBUG */ + +#if 0 +static void debug_print (const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static void +debug_print (const char *fmt, ...) +{ +#ifdef VERIFY_DEBUG + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); +#endif /* VERIFY_DEBUG */ +} +#endif + +/* This started as a fairly ordinary verifier, and for the most part + it remains so. It works in the obvious way, by modeling the effect + of each opcode as it is encountered. For most opcodes, this is a + straightforward operation. + + This verifier does not do type merging. It used to, but this + results in difficulty verifying some relatively simple code + involving interfaces, and it pushed some verification work into the + interpreter. + + Instead of merging reference types, when we reach a point where two + flows of control merge, we simply keep the union of reference types + from each branch. Then, when we need to verify a fact about a + reference on the stack (e.g., that it is compatible with the + argument type of a method), we check to ensure that all possible + types satisfy the requirement. + + Another area this verifier differs from the norm is in its handling + of subroutines. The JVM specification has some confusing things to + say about subroutines. For instance, it makes claims about not + allowing subroutines to merge and it rejects recursive subroutines. + For the most part these are red herrings; we used to try to follow + these things but they lead to problems. For example, the notion of + "being in a subroutine" is not well-defined: is an exception + handler in a subroutine? If you never execute the `ret' but + instead `goto 1' do you remain in the subroutine? + + For clarity on what is really required for type safety, read + "Simple Verification Technique for Complex Java Bytecode + Subroutines" by Alessandro Coglio. Among other things this paper + shows that recursive subroutines are not harmful to type safety. + We implement something similar to what he proposes. Note that this + means that this verifier will accept code that is rejected by some + other verifiers. + + For those not wanting to read the paper, the basic observation is + that we can maintain split states in subroutines. We maintain one + state for each calling `jsr'. In other words, we re-verify a + subroutine once for each caller, using the exact types held by the + callers (as opposed to the old approach of merging types and + keeping a bitmap registering what did or did not change). This + approach lets us continue to verify correctly even when a + subroutine is exited via `goto' or `athrow' and not `ret'. + + In some other areas the JVM specification is (mildly) incorrect, + so we diverge. For instance, you cannot + violate type safety by allocating an object with `new' and then + failing to initialize it, no matter how one branches or where one + stores the uninitialized reference. See "Improving the official + specification of Java bytecode verification" by Alessandro Coglio. + + Note that there's no real point in enforcing that padding bytes or + the mystery byte of invokeinterface must be 0, but we do that + regardless. + + The verifier is currently neither completely lazy nor eager when it + comes to loading classes. It tries to represent types by name when + possible, and then loads them when it needs to verify a fact about + the type. Checking types by name is valid because we only use + names which come from the current class' constant pool. Since all + such names are looked up using the same class loader, there is no + danger that we might be fooled into comparing different types with + the same name. + + In the future we plan to allow for a completely lazy mode of + operation, where the verifier will construct a list of type + assertions to be checked later. + + Some test cases for the verifier live in the "verify" module of the + Mauve test suite. However, some of these are presently + (2004-01-20) believed to be incorrect. (More precisely the notion + of "correct" is not well-defined, and this verifier differs from + others while remaining type-safe.) Some other tests live in the + libgcj test suite. + + This verifier is also written to be pluggable. This means that it + is intended for use in a variety of environments, not just libgcj. + As a result the verifier expects a number of type and method + declarations to be declared in "verify.h". The intent is that you + recompile the verifier for your particular environment. This + approach was chosen so that operations could be inlined in verify.h + as much as possible. + + See the verify.h that accompanies this copy of the verifier to see + what types, preprocessor defines, and functions must be declared. + The interface is ad hoc, but was defined so that it could be + implemented to connect to a pure C program. +*/ + +#define FLAG_INSN_START 1 +#define FLAG_BRANCH_TARGET 2 +#define FLAG_INSN_SEEN 4 + +struct state; +struct type; +struct ref_intersection; + +typedef struct state state; +typedef struct type type; +typedef struct ref_intersection ref_intersection; + +/*typedef struct state_list state_list;*/ + +typedef struct state_list +{ + state *val; + struct state_list *next; +} state_list; + +typedef struct vfy_string_list +{ + vfy_string val; + struct vfy_string_list *next; +} vfy_string_list; + +typedef struct verifier_context +{ + /* The current PC. */ + int PC; + /* The PC corresponding to the start of the current instruction. */ + int start_PC; + + /* The current state of the stack, locals, etc. */ + state *current_state; + + /* At each branch target we keep a linked list of all the states we + can process at that point. We'll only have multiple states at a + given PC if they both have different return-address types in the + same stack or local slot. This array is indexed by PC and holds + the list of all such states. */ + state_list **states; + + /* We keep a linked list of all the states which we must reverify. + This is the head of the list. */ + state *next_verify_state; + + /* We keep some flags for each instruction. The values are the + FLAG_* constants defined above. This is an array indexed by PC. */ + char *flags; + + /* The bytecode itself. */ + const unsigned char *bytecode; + /* The exceptions. */ + vfy_exception *exception; + + /* Defining class. */ + vfy_jclass current_class; + /* This method. */ + vfy_method *current_method; + + /* A linked list of utf8 objects we allocate. */ + vfy_string_list *utf8_list; + + /* A linked list of all ref_intersection objects we allocate. */ + ref_intersection *isect_list; +} verifier_context; + +/* The current verifier's state data. This is maintained by + {push/pop}_verifier_context to provide a shorthand form to access + the verification state. */ +static GTY(()) verifier_context *vfr; + +/* Local function declarations. */ +bool type_initialized (type *t); +int ref_count_dimensions (ref_intersection *ref); + +#if 0 + /* Create a new Utf-8 constant and return it. We do this to avoid + having our Utf-8 constants prematurely collected. */ + static vfy_string + make_utf8_const (const char *s, int len) + { + vfy_string val = vfy_make_string (s, len); + vfy_string_list *lu = vfy_alloc (sizeof (vfy_string_list)); + lu->val = val; + lu->next = vfr->utf8_list; + vfr->utf8_list = lu; + + return val; + } +#endif + +static void +verify_fail_pc (const char *s, int pc) +{ + vfy_fail (s, pc, vfr->current_class, vfr->current_method); +} + +static void +verify_fail (const char *s) +{ + verify_fail_pc (s, -1); +} + +/* This enum holds a list of tags for all the different types we + need to handle. Reference types are treated specially by the + type class. */ +typedef enum type_val +{ + void_type, + + /* The values for primitive types are chosen to correspond to values + specified to newarray. */ + boolean_type = 4, + char_type = 5, + float_type = 6, + double_type = 7, + byte_type = 8, + short_type = 9, + int_type = 10, + long_type = 11, + + /* Used when overwriting second word of a double or long in the + local variables. Also used after merging local variable states + to indicate an unusable value. */ + unsuitable_type, + return_address_type, + /* This is the second word of a two-word value, i.e., a double or + a long. */ + continuation_type, + + /* Everything after `reference_type' must be a reference type. */ + reference_type, + null_type, + uninitialized_reference_type +} type_val; + +/* This represents a merged class type. Some verifiers (including + earlier versions of this one) will compute the intersection of + two class types when merging states. However, this loses + critical information about interfaces implemented by the various + classes. So instead we keep track of all the actual classes that + have been merged. */ +struct ref_intersection +{ + /* Whether or not this type has been resolved. */ + bool is_resolved; + + /* Actual type data. */ + union + { + /* For a resolved reference type, this is a pointer to the class. */ + vfy_jclass klass; + /* For other reference types, this it the name of the class. */ + vfy_string name; + } data; + + /* Link to the next reference in the intersection. */ + ref_intersection *ref_next; + + /* This is used to keep track of all the allocated + ref_intersection objects, so we can free them. + FIXME: we should allocate these in chunks. */ + ref_intersection *alloc_next; +}; + +static ref_intersection * +make_ref (void) +{ + ref_intersection *new_ref = + (ref_intersection *) vfy_alloc (sizeof (ref_intersection)); + + new_ref->alloc_next = vfr->isect_list; + vfr->isect_list = new_ref; + return new_ref; +} + +static ref_intersection * +clone_ref (ref_intersection *dup) +{ + ref_intersection *new_ref = make_ref (); + + new_ref->is_resolved = dup->is_resolved; + new_ref->data = dup->data; + return new_ref; +} + +static void +resolve_ref (ref_intersection *ref) +{ + if (ref->is_resolved) + return; + ref->data.klass = vfy_find_class (vfr->current_class, ref->data.name); + ref->is_resolved = true; +} + +static bool +refs_equal (ref_intersection *ref1, ref_intersection *ref2) +{ + if (! ref1->is_resolved && ! ref2->is_resolved + && vfy_strings_equal (ref1->data.name, ref2->data.name)) + return true; + if (! ref1->is_resolved) + resolve_ref (ref1); + if (! ref2->is_resolved) + resolve_ref (ref2); + return ref1->data.klass == ref2->data.klass; +} + +/* Merge REF1 type into REF2, returning the result. This will + return REF2 if all the classes in THIS already appear in + REF2. */ +static ref_intersection * +merge_refs (ref_intersection *ref1, ref_intersection *ref2) +{ + ref_intersection *tail = ref2; + for (; ref1 != NULL; ref1 = ref1->ref_next) + { + bool add = true; + ref_intersection *iter; + for (iter = ref2; iter != NULL; iter = iter->ref_next) + { + if (refs_equal (ref1, iter)) + { + add = false; + break; + } + } + + if (add) + { + ref_intersection *new_tail = clone_ref (ref1); + new_tail->ref_next = tail; + tail = new_tail; + } + } + return tail; +} + +/* See if an object of type SOURCE can be assigned to an object of + type TARGET. This might resolve classes in one chain or the other. */ +static bool +ref_compatible (ref_intersection *target, ref_intersection *source) +{ + for (; target != NULL; target = target->ref_next) + { + ref_intersection *source_iter = source; + + for (; source_iter != NULL; source_iter = source_iter->ref_next) + { + /* Avoid resolving if possible. */ + if (! target->is_resolved + && ! source_iter->is_resolved + && vfy_strings_equal (target->data.name, + source_iter->data.name)) + continue; + + if (! target->is_resolved) + resolve_ref (target); + if (! source_iter->is_resolved) + resolve_ref (source_iter); + + if (! vfy_is_assignable_from (target->data.klass, + source_iter->data.klass)) + return false; + } + } + + return true; +} + +static bool +ref_isarray (ref_intersection *ref) +{ + /* assert (ref_next == NULL); */ + if (ref->is_resolved) + return vfy_is_array (ref->data.klass); + else + return vfy_string_bytes (ref->data.name)[0] == '['; +} + +static bool +ref_isinterface (ref_intersection *ref) +{ + /* assert (ref_next == NULL); */ + if (! ref->is_resolved) + resolve_ref (ref); + return vfy_is_interface (ref->data.klass); +} + +static bool +ref_isabstract (ref_intersection *ref) +{ + /* assert (ref_next == NULL); */ + if (! ref->is_resolved) + resolve_ref (ref); + return vfy_is_abstract (ref->data.klass); +} + +static vfy_jclass +ref_getclass (ref_intersection *ref) +{ + if (! ref->is_resolved) + resolve_ref (ref); + return ref->data.klass; +} + +int +ref_count_dimensions (ref_intersection *ref) +{ + int ndims = 0; + if (ref->is_resolved) + { + vfy_jclass k = ref->data.klass; + while (vfy_is_array (k)) + { + k = vfy_get_component_type (k); + ++ndims; + } + } + else + { + const char *p = vfy_string_bytes (ref->data.name); + while (*p++ == '[') + ++ndims; + } + return ndims; +} + +/* Return the type_val corresponding to a primitive signature + character. For instance `I' returns `int.class'. */ +static type_val +get_type_val_for_signature (char sig) +{ + type_val rt; + switch (sig) + { + case 'Z': + rt = boolean_type; + break; + case 'B': + rt = byte_type; + break; + case 'C': + rt = char_type; + break; + case 'S': + rt = short_type; + break; + case 'I': + rt = int_type; + break; + case 'J': + rt = long_type; + break; + case 'F': + rt = float_type; + break; + case 'D': + rt = double_type; + break; + case 'V': + rt = void_type; + break; + default: + verify_fail ("invalid signature"); + return null_type; + } + return rt; +} + +/* Return the type_val corresponding to a primitive class. */ +static type_val +get_type_val_for_primtype (vfy_jclass k) +{ + return get_type_val_for_signature (vfy_get_primitive_char (k)); +} + +/* The `type' class is used to represent a single type in the verifier. */ +struct type +{ + /* The type key. */ + type_val key; + + /* For reference types, the representation of the type. */ + ref_intersection *klass; + + /* This is used in two situations. + + First, when constructing a new object, it is the PC of the + `new' instruction which created the object. We use the special + value UNINIT to mean that this is uninitialized, and the + special value SELF for the case where the current method is + itself the <init> method. + + Second, when the key is return_address_type, this holds the PC + of the instruction following the `jsr'. */ + int pc; + + #define UNINIT -2 + #define SELF -1 +}; + +#if 0 +/* Basic constructor. */ +static void +init_type (type *t) +{ + t->key = unsuitable_type; + t->klass = NULL; + t->pc = UNINIT; +} +#endif + +/* Make a new instance given the type tag. We assume a generic + `reference_type' means Object. */ +static void +init_type_from_tag (type *t, type_val k) +{ + t->key = k; + /* For reference_type, if KLASS==NULL then that means we are + looking for a generic object of any kind, including an + uninitialized reference. */ + t->klass = NULL; + t->pc = UNINIT; +} + +/* Make a type for the given type_val tag K. */ +static type +make_type (type_val k) +{ + type t; + init_type_from_tag (&t, k); + return t; +} + +/* Make a new instance given a class. */ +static void +init_type_from_class (type *t, vfy_jclass k) +{ + t->key = reference_type; + t->klass = make_ref (); + t->klass->is_resolved = true; + t->klass->data.klass = k; + t->klass->ref_next = NULL; + t->pc = UNINIT; +} + +static type +make_type_from_class (vfy_jclass k) +{ + type t; + init_type_from_class (&t, k); + return t; +} + +static void +init_type_from_string (type *t, vfy_string n) +{ + t->key = reference_type; + t->klass = make_ref (); + t->klass->is_resolved = false; + t->klass->data.name = n; + t->klass->ref_next = NULL; + t->pc = UNINIT; +} + +static type +make_type_from_string (vfy_string n) +{ + type t; + init_type_from_string (&t, n); + return t; +} + +#if 0 + /* Make a new instance given the name of a class. */ + type (vfy_string n) + { + key = reference_type; + klass = new ref_intersection (n, verifier); + pc = UNINIT; + } + + /* Copy constructor. */ + static type copy_type (type *t) + { + type copy; + copy.key = t->key; + copy.klass = t->klass; + copy.pc = t->pc; + return copy; + } +#endif + +/* Promote a numeric type. */ +static void +vfy_promote_type (type *t) +{ + if (t->key == boolean_type || t->key == char_type + || t->key == byte_type || t->key == short_type) + t->key = int_type; +} +#define promote_type vfy_promote_type + +/* Mark this type as the uninitialized result of `new'. */ +static void +type_set_uninitialized (type *t, int npc) +{ + if (t->key == reference_type) + t->key = uninitialized_reference_type; + else + verify_fail ("internal error in type::uninitialized"); + t->pc = npc; +} + +/* Mark this type as now initialized. */ +static void +type_set_initialized (type *t, int npc) +{ + if (npc != UNINIT && t->pc == npc && t->key == uninitialized_reference_type) + { + t->key = reference_type; + t->pc = UNINIT; + } +} + +/* Mark this type as a particular return address. */ +static void type_set_return_address (type *t, int npc) +{ + t->pc = npc; +} + +/* Return true if this type and type OTHER are considered + mergeable for the purposes of state merging. This is related + to subroutine handling. For this purpose two types are + considered unmergeable if they are both return-addresses but + have different PCs. */ +static bool +type_state_mergeable_p (type *t1, type *t2) +{ + return (t1->key != return_address_type + || t2->key != return_address_type + || t1->pc == t2->pc); +} + +/* Return true if an object of type K can be assigned to a variable + of type T. Handle various special cases too. Might modify + T or K. Note however that this does not perform numeric + promotion. */ +static bool +types_compatible (type *t, type *k) +{ + /* Any type is compatible with the unsuitable type. */ + if (k->key == unsuitable_type) + return true; + + if (t->key < reference_type || k->key < reference_type) + return t->key == k->key; + + /* The `null' type is convertible to any initialized reference + type. */ + if (t->key == null_type) + return k->key != uninitialized_reference_type; + if (k->key == null_type) + return t->key != uninitialized_reference_type; + + /* A special case for a generic reference. */ + if (t->klass == NULL) + return true; + if (k->klass == NULL) + verify_fail ("programmer error in type::compatible"); + + /* An initialized type and an uninitialized type are not + compatible. */ + if (type_initialized (t) != type_initialized (k)) + return false; + + /* Two uninitialized objects are compatible if either: + * The PCs are identical, or + * One PC is UNINIT. */ + if (type_initialized (t)) + { + if (t->pc != k->pc && t->pc != UNINIT && k->pc != UNINIT) + return false; + } + + return ref_compatible (t->klass, k->klass); +} + +static bool +type_isvoid (type *t) +{ + return t->key == void_type; +} + +static bool +type_iswide (type *t) +{ + return t->key == long_type || t->key == double_type; +} + +/* Return number of stack or local variable slots taken by this type. */ +static int +type_depth (type *t) +{ + return type_iswide (t) ? 2 : 1; +} + +static bool +type_isarray (type *t) +{ + /* We treat null_type as not an array. This is ok based on the + current uses of this method. */ + if (t->key == reference_type) + return ref_isarray (t->klass); + return false; +} + +static bool +type_isnull (type *t) +{ + return t->key == null_type; +} + +static bool +type_isinterface (type *t) +{ + if (t->key != reference_type) + return false; + return ref_isinterface (t->klass); +} + +static bool +type_isabstract (type *t) +{ + if (t->key != reference_type) + return false; + return ref_isabstract (t->klass); +} + +/* Return the element type of an array. */ +static type +type_array_element (type *t) +{ + type et; + vfy_jclass k; + + if (t->key != reference_type) + verify_fail ("programmer error in type::element_type()"); + + k = vfy_get_component_type (ref_getclass (t->klass)); + if (vfy_is_primitive (k)) + init_type_from_tag (&et, get_type_val_for_primtype (k)); + else + init_type_from_class (&et, k); + return et; +} + +/* Return the array type corresponding to an initialized + reference. We could expand this to work for other kinds of + types, but currently we don't need to. */ +static type +type_to_array (type *t) +{ + type at; + vfy_jclass k; + + if (t->key != reference_type) + verify_fail ("internal error in type::to_array()"); + + k = ref_getclass (t->klass); + init_type_from_class (&at, vfy_get_array_class (k)); + return at; +} + +static bool +type_isreference (type *t) +{ + return t->key >= reference_type; +} + +static int +type_get_pc (type *t) +{ + return t->pc; +} + +bool +type_initialized (type *t) +{ + return t->key == reference_type || t->key == null_type; +} + +#if 0 +static bool +type_isresolved (type *t) +{ + return (t->key == reference_type + || t->key == null_type + || t->key == uninitialized_reference_type); +} +#endif + +static void +type_verify_dimensions (type *t, int ndims) +{ + /* The way this is written, we don't need to check isarray(). */ + if (t->key != reference_type) + verify_fail ("internal error in verify_dimensions:" + " not a reference type"); + + if (ref_count_dimensions (t->klass) < ndims) + verify_fail ("array type has fewer dimensions" + " than required"); +} + +/* Merge OLD_TYPE into this. On error throw exception. Return + true if the merge caused a type change. */ +static bool +merge_types (type *t, type *old_type, bool local_semantics) +{ + bool changed = false; + bool refo = type_isreference (old_type); + bool refn = type_isreference (t); + if (refo && refn) + { + if (old_type->key == null_type) + ; + else if (t->key == null_type) + { + *t = *old_type; + changed = true; + } + else if (type_initialized (t) != type_initialized (old_type)) + verify_fail ("merging initialized and uninitialized types"); + else + { + ref_intersection *merged; + if (! type_initialized (t)) + { + if (t->pc == UNINIT) + t->pc = old_type->pc; + else if (old_type->pc == UNINIT) + ; + else if (t->pc != old_type->pc) + verify_fail ("merging different uninitialized types"); + } + + merged = merge_refs (old_type->klass, t->klass); + if (merged != t->klass) + { + t->klass = merged; + changed = true; + } + } + } + else if (refo || refn || t->key != old_type->key) + { + if (local_semantics) + { + /* If we already have an `unsuitable' type, then we + don't need to change again. */ + if (t->key != unsuitable_type) + { + t->key = unsuitable_type; + changed = true; + } + } + else + verify_fail ("unmergeable type"); + } + return changed; +} + +#ifdef VERIFY_DEBUG +static void +type_print (type *t) +{ + char c = '?'; + switch (t->key) + { + case boolean_type: c = 'Z'; break; + case byte_type: c = 'B'; break; + case char_type: c = 'C'; break; + case short_type: c = 'S'; break; + case int_type: c = 'I'; break; + case long_type: c = 'J'; break; + case float_type: c = 'F'; break; + case double_type: c = 'D'; break; + case void_type: c = 'V'; break; + case unsuitable_type: c = '-'; break; + case return_address_type: c = 'r'; break; + case continuation_type: c = '+'; break; + case reference_type: c = 'L'; break; + case null_type: c = '@'; break; + case uninitialized_reference_type: c = 'U'; break; + } + debug_print ("%c", c); +} +#endif /* VERIFY_DEBUG */ + +/* This class holds all the state information we need for a given + location. */ +struct state +{ + /* The current top of the stack, in terms of slots. */ + int stacktop; + /* The current depth of the stack. This will be larger than + STACKTOP when wide types are on the stack. */ + int stackdepth; + /* The stack. */ + type *stack; + /* The local variables. */ + type *locals; + /* We keep track of the type of `this' specially. This is used to + ensure that an instance initializer invokes another initializer + on `this' before returning. We must keep track of this + specially because otherwise we might be confused by code which + assigns to locals[0] (overwriting `this') and then returns + without really initializing. */ + type this_type; + + /* The PC for this state. This is only valid on states which are + permanently attached to a given PC. For an object like + `current_state', which is used transiently, this has no + meaning. */ + int pc; + /* We keep a linked list of all states requiring reverification. + If this is the special value INVALID_STATE then this state is + not on the list. NULL marks the end of the linked list. */ + state *next; +}; + +/* NO_NEXT is the PC value meaning that a new state must be + acquired from the verification list. */ +#define NO_NEXT -1 + +#if 0 +static void +init_state (state *s) +{ + s->stack = NULL; + s->locals = NULL; + s->next = INVALID_STATE; +} +#endif + +static void +init_state_with_stack (state *s, int max_stack, int max_locals) +{ + int i; + s->stacktop = 0; + s->stackdepth = 0; + s->stack = (type *) vfy_alloc (max_stack * sizeof (type)); + for (i = 0; i < max_stack; ++i) + init_type_from_tag (&s->stack[i], unsuitable_type); + s->locals = (type *) vfy_alloc (max_locals * sizeof (type)); + for (i = 0; i < max_locals; ++i) + init_type_from_tag (&s->locals[i], unsuitable_type); + init_type_from_tag (&s->this_type, unsuitable_type); + s->pc = NO_NEXT; + s->next = INVALID_STATE; +} + +static void +copy_state (state *s, state *copy, int max_stack, int max_locals) +{ + int i; + s->stacktop = copy->stacktop; + s->stackdepth = copy->stackdepth; + for (i = 0; i < max_stack; ++i) + s->stack[i] = copy->stack[i]; + for (i = 0; i < max_locals; ++i) + s->locals[i] = copy->locals[i]; + + s->this_type = copy->this_type; + /* Don't modify `next' or `pc'. */ +} + +static void +copy_state_with_stack (state *s, state *orig, int max_stack, int max_locals) +{ + init_state_with_stack (s, max_stack, max_locals); + copy_state (s, orig, max_stack, max_locals); +} + +/* Allocate a new state, copying ORIG. */ +static state * +make_state_copy (state *orig, int max_stack, int max_locals) +{ + state *s = vfy_alloc (sizeof (state)); + copy_state_with_stack (s, orig, max_stack, max_locals); + return s; +} + +static state * +make_state (int max_stack, int max_locals) +{ + state *s = vfy_alloc (sizeof (state)); + init_state_with_stack (s, max_stack, max_locals); + return s; +} + +#if 0 +static void +free_state (state *s) +{ + if (s->stack != NULL) + vfy_free (s->stack); + if (s->locals != NULL) + vfy_free (s->locals); +} +#endif + +#if 0 + void *operator new[] (size_t bytes) + { + return vfy_alloc (bytes); + } + + void operator delete[] (void *mem) + { + vfy_free (mem); + } + + void *operator new (size_t bytes) + { + return vfy_alloc (bytes); + } + + void operator delete (void *mem) + { + vfy_free (mem); + } +#endif + +/* Modify this state to reflect entry to an exception handler. */ +static void +state_set_exception (state *s, type *t, int max_stack) +{ + int i; + s->stackdepth = 1; + s->stacktop = 1; + s->stack[0] = *t; + for (i = s->stacktop; i < max_stack; ++i) + init_type_from_tag (&s->stack[i], unsuitable_type); +} + +static int +state_get_pc (state *s) +{ + return s->pc; +} + +#if 0 +static void +set_pc (state *s, int npc) +{ + s->pc = npc; +} +#endif + +/* Merge STATE_OLD into this state. Destructively modifies this + state. Returns true if the new state was in fact changed. + Will throw an exception if the states are not mergeable. */ +static bool +merge_states (state *s, state *state_old, int max_locals) +{ + int i; + bool changed = false; + + /* Special handling for `this'. If one or the other is + uninitialized, then the merge is uninitialized. */ + if (type_initialized (&s->this_type)) + s->this_type = state_old->this_type; + + /* Merge stacks. */ + if (state_old->stacktop != s->stacktop) /* FIXME stackdepth instead? */ + verify_fail ("stack sizes differ"); + for (i = 0; i < state_old->stacktop; ++i) + { + if (merge_types (&s->stack[i], &state_old->stack[i], false)) + changed = true; + } + + /* Merge local variables. */ + for (i = 0; i < max_locals; ++i) + { + if (merge_types (&s->locals[i], &state_old->locals[i], true)) + changed = true; + } + + return changed; +} + +/* Ensure that `this' has been initialized. */ +static void +state_check_this_initialized (state *s) +{ + if (type_isreference (&s->this_type) && ! type_initialized (&s->this_type)) + verify_fail ("`this' is uninitialized"); +} + +/* Set type of `this'. */ +static void +state_set_this_type (state *s, type *k) +{ + s->this_type = *k; +} + +/* Mark each `new'd object we know of that was allocated at PC as + initialized. */ +static void +state_set_initialized (state *s, int pc, int max_locals) +{ + int i; + for (i = 0; i < s->stacktop; ++i) + type_set_initialized (&s->stack[i], pc); + for (i = 0; i < max_locals; ++i) + type_set_initialized (&s->locals[i], pc); + type_set_initialized (&s->this_type, pc); +} + +/* This tests to see whether two states can be considered "merge + compatible". If both states have a return-address in the same + slot, and the return addresses are different, then they are not + compatible and we must not try to merge them. */ +static bool +state_mergeable_p (state *s, state *other, int max_locals) + +{ + int i; + + /* This is tricky: if the stack sizes differ, then not only are + these not mergeable, but in fact we should give an error, as + we've found two execution paths that reach a branch target + with different stack depths. FIXME stackdepth instead? */ + if (s->stacktop != other->stacktop) + verify_fail ("stack sizes differ"); + + for (i = 0; i < s->stacktop; ++i) + if (! type_state_mergeable_p (&s->stack[i], &other->stack[i])) + return false; + for (i = 0; i < max_locals; ++i) + if (! type_state_mergeable_p (&s->locals[i], &other->locals[i])) + return false; + return true; +} + +static void +state_reverify (state *s) +{ + if (s->next == INVALID_STATE) + { + s->next = vfr->next_verify_state; + vfr->next_verify_state = s; + } +} + +#ifdef VERIFY_DEBUG +static void +debug_print_state (state *s, const char *leader, int pc, int max_stack, + int max_locals) +{ + int i; + debug_print ("%s [%4d]: [stack] ", leader, pc); + for (i = 0; i < s->stacktop; ++i) + type_print (&s->stack[i]); + for (; i < max_stack; ++i) + debug_print ("."); + debug_print (" [local] "); + for (i = 0; i < max_locals; ++i) + type_print (&s->locals[i]); + debug_print (" | %p\n", s); +} +#else +static void +debug_print_state (state *s ATTRIBUTE_UNUSED, + const char *leader ATTRIBUTE_UNUSED, + int pc ATTRIBUTE_UNUSED, int max_stack ATTRIBUTE_UNUSED, + int max_locals ATTRIBUTE_UNUSED) +{ +} +#endif /* VERIFY_DEBUG */ + +static type +pop_raw (void) +{ + type r; + state *s = vfr->current_state; + if (s->stacktop <= 0) + verify_fail ("stack empty"); + r = s->stack[--s->stacktop]; + s->stackdepth -= type_depth (&r); + if (s->stackdepth < 0) + verify_fail_pc ("stack empty", vfr->start_PC); + return r; +} + +static type +pop32 (void) +{ + type r = pop_raw (); + if (type_iswide (&r)) + verify_fail ("narrow pop of wide type"); + return r; +} + +static type +vfy_pop_type_t (type match) +{ + type t; + vfy_promote_type (&match); + t = pop_raw (); + if (! types_compatible (&match, &t)) + verify_fail ("incompatible type on stack"); + return t; +} + +static type +vfy_pop_type (type_val match) +{ + type t = make_type (match); + return vfy_pop_type_t (t); +} + +#define pop_type vfy_pop_type +#define pop_type_t vfy_pop_type_t + +/* Pop a reference which is guaranteed to be initialized. MATCH + doesn't have to be a reference type; in this case this acts like + pop_type. */ +static type +pop_init_ref_t (type match) +{ + type t = pop_raw (); + if (type_isreference (&t) && ! type_initialized (&t)) + verify_fail ("initialized reference required"); + else if (! types_compatible (&match, &t)) + verify_fail ("incompatible type on stack"); + return t; +} + +static type +pop_init_ref (type_val match) +{ + type t = make_type (match); + return pop_init_ref_t (t); +} + +/* Pop a reference type or a return address. */ +static type +pop_ref_or_return (void) +{ + type t = pop_raw (); + if (! type_isreference (&t) && t.key != return_address_type) + verify_fail ("expected reference or return address on stack"); + return t; +} + +static void +vfy_push_type_t (type t) +{ + int depth; + state *s = vfr->current_state; + /* If T is a numeric type like short, promote it to int. */ + promote_type (&t); + + depth = type_depth (&t); + + if (s->stackdepth + depth > vfr->current_method->max_stack) + verify_fail ("stack overflow"); + s->stack[s->stacktop++] = t; + s->stackdepth += depth; +} + +static void +vfy_push_type (type_val tval) +{ + type t = make_type (tval); + return vfy_push_type_t (t); +} + +#define push_type vfy_push_type +#define push_type_t vfy_push_type_t + +static void +set_variable (int index, type t) +{ + int depth; + state *s = vfr->current_state; + /* If T is a numeric type like short, promote it to int. */ + promote_type (&t); + + depth = type_depth (&t); + if (index > vfr->current_method->max_locals - depth) + verify_fail ("invalid local variable"); + s->locals[index] = t; + + if (depth == 2) + init_type_from_tag (&s->locals[index + 1], continuation_type); + if (index > 0 && type_iswide (&s->locals[index - 1])) + init_type_from_tag (&s->locals[index - 1], unsuitable_type); +} + +static type +get_variable_t (int index, type *t) +{ + state *s = vfr->current_state; + int depth = type_depth (t); + if (index > vfr->current_method->max_locals - depth) + verify_fail ("invalid local variable"); + if (! types_compatible (t, &s->locals[index])) + verify_fail ("incompatible type in local variable"); + if (depth == 2) + { + type cont = make_type (continuation_type); + if (! types_compatible (&s->locals[index + 1], &cont)) + verify_fail ("invalid local variable"); + } + return s->locals[index]; +} + +static type +get_variable (int index, type_val v) +{ + type t = make_type (v); + return get_variable_t (index, &t); +} + +/* Make sure ARRAY is an array type and that its elements are + compatible with type ELEMENT. Returns the actual element type. */ +static type +require_array_type_t (type array, type element) +{ + type t; + /* An odd case. Here we just pretend that everything went ok. If + the requested element type is some kind of reference, return + the null type instead. */ + if (type_isnull (&array)) + return type_isreference (&element) ? make_type (null_type) : element; + + if (! type_isarray (&array)) + verify_fail ("array required"); + + t = type_array_element (&array); + if (! types_compatible (&element, &t)) + { + /* Special case for byte arrays, which must also be boolean + arrays. */ + bool ok = true; + if (element.key == byte_type) + { + type e2 = make_type (boolean_type); + ok = types_compatible (&e2, &t); + } + if (! ok) + verify_fail ("incompatible array element type"); + } + + /* Return T and not ELEMENT, because T might be specialized. */ + return t; +} + +static type +require_array_type (type array, type_val element) +{ + type t = make_type (element); + return require_array_type_t (array, t); +} + +static jint +get_byte (void) +{ + if (vfr->PC >= vfr->current_method->code_length) + verify_fail ("premature end of bytecode"); + return (jint) vfr->bytecode[vfr->PC++] & 0xff; +} + +static jint +get_ushort (void) +{ + jint b1 = get_byte (); + jint b2 = get_byte (); + return (jint) ((b1 << 8) | b2) & 0xffff; +} + +static jint +get_short (void) +{ + jint b1 = get_byte (); + jint b2 = get_byte (); + jshort s = (b1 << 8) | b2; + return (jint) s; +} + +static jint +get_int (void) +{ + jint b1 = get_byte (); + jint b2 = get_byte (); + jint b3 = get_byte (); + jint b4 = get_byte (); + return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; +} + +static int +compute_jump (int offset) +{ + int npc = vfr->start_PC + offset; + if (npc < 0 || npc >= vfr->current_method->code_length) + verify_fail_pc ("branch out of range", vfr->start_PC); + return npc; +} + +/* Add a new state to the state list at NPC. */ +static state * +add_new_state (int npc, state *old_state) +{ + state_list *nlink; + vfy_method *current_method = vfr->current_method; + state *new_state = make_state_copy (old_state, current_method->max_stack, + current_method->max_locals); + debug_print ("== New state in add_new_state\n"); + debug_print_state (new_state, "New", npc, current_method->max_stack, + current_method->max_locals); + + nlink = vfy_alloc (sizeof (state_list)); + nlink->val = new_state; + nlink->next = vfr->states[npc]; + vfr->states[npc] = nlink; + new_state->pc = npc; + return new_state; +} + +/* Merge the indicated state into the state at the branch target and + schedule a new PC if there is a change. NPC is the PC of the + branch target, and FROM_STATE is the state at the source of the + branch. This method returns true if the destination state + changed and requires reverification, false otherwise. */ +static void +merge_into (int npc, state *from_state) +{ + /* Iterate over all target states and merge our state into each, + if applicable. FIXME one improvement we could make here is + "state destruction". Merging a new state into an existing one + might cause a return_address_type to be merged to + unsuitable_type. In this case the resulting state may now be + mergeable with other states currently held in parallel at this + location. So in this situation we could pairwise compare and + reduce the number of parallel states. */ + state_list *iter; + bool applicable = false; + for (iter = vfr->states[npc]; iter != NULL; iter = iter->next) + { + state *new_state = iter->val; + vfy_method *current_method = vfr->current_method; + + if (state_mergeable_p (new_state, from_state, + current_method->max_locals)) + { + bool changed; + applicable = true; + + debug_print ("== Merge states in merge_into\n"); + debug_print_state (from_state, "Frm", vfr->start_PC, current_method->max_stack, + current_method->max_locals); + debug_print_state (new_state, " To", npc, current_method->max_stack, + current_method->max_locals); + changed = merge_states (new_state, from_state, + current_method->max_locals); + debug_print_state (new_state, "New", npc, current_method->max_stack, + current_method->max_locals); + + if (changed) + state_reverify (new_state); + } + } + + if (! applicable) + { + /* Either we don't yet have a state at NPC, or we have a + return-address type that is in conflict with all existing + state. So, we need to create a new entry. */ + state *new_state = add_new_state (npc, from_state); + /* A new state added in this way must always be reverified. */ + state_reverify (new_state); + } +} + +static void +push_jump (int offset) +{ + int npc = compute_jump (offset); + /* According to the JVM Spec, we need to check for uninitialized + objects here. However, this does not actually affect type + safety, and the Eclipse java compiler generates code that + violates this constraint. */ + merge_into (npc, vfr->current_state); +} + +static void +push_exception_jump (type t, int pc) +{ + state s; + /* According to the JVM Spec, we need to check for uninitialized + objects here. However, this does not actually affect type + safety, and the Eclipse java compiler generates code that + violates this constraint. */ + copy_state_with_stack (&s, vfr->current_state, + vfr->current_method->max_stack, + vfr->current_method->max_locals); + if (vfr->current_method->max_stack < 1) + verify_fail ("stack overflow at exception handler"); + state_set_exception (&s, &t, vfr->current_method->max_stack); + merge_into (pc, &s); + /* FIXME: leak.. need free_state or GC */ +} + +static state * +pop_jump (void) +{ + state *new_state = vfr->next_verify_state; + if (new_state == INVALID_STATE) + verify_fail ("programmer error in pop_jump"); + if (new_state != NULL) + { + vfr->next_verify_state = new_state->next; + new_state->next = INVALID_STATE; + } + return new_state; +} + +static void +invalidate_pc (void) +{ + vfr->PC = NO_NEXT; +} + +static void +note_branch_target (int pc) +{ + /* Don't check `pc <= PC', because we've advanced PC after + fetching the target and we haven't yet checked the next + instruction. */ + if (pc < vfr->PC && ! (vfr->flags[pc] & FLAG_INSN_START)) + verify_fail_pc ("branch not to instruction start", vfr->start_PC); + vfr->flags[pc] |= FLAG_BRANCH_TARGET; +} + +static void +skip_padding (void) +{ + while ((vfr->PC % 4) > 0) + if (get_byte () != 0) + verify_fail ("found nonzero padding byte"); +} + +/* Do the work for a `ret' instruction. INDEX is the index into the + local variables. */ +static void +handle_ret_insn (int index) +{ + type ret = make_type (return_address_type); + type ret_addr = get_variable_t (index, &ret); + /* It would be nice if we could do this. However, the JVM Spec + doesn't say that this is what happens. It is implied that + reusing a return address is invalid, but there's no actual + prohibition against it. */ + /* set_variable (index, unsuitable_type); */ + + int npc = type_get_pc (&ret_addr); + /* We might be returning to a `jsr' that is at the end of the + bytecode. This is ok if we never return from the called + subroutine, but if we see this here it is an error. */ + if (npc >= vfr->current_method->code_length) + verify_fail ("fell off end"); + + /* According to the JVM Spec, we need to check for uninitialized + objects here. However, this does not actually affect type + safety, and the Eclipse java compiler generates code that + violates this constraint. */ + merge_into (npc, vfr->current_state); + invalidate_pc (); +} + +static void handle_jsr_insn (int offset) +{ + type ret_addr; + int npc = compute_jump (offset); + + /* According to the JVM Spec, we need to check for uninitialized + objects here. However, this does not actually affect type + safety, and the Eclipse java compiler generates code that + violates this constraint. */ + + /* Modify our state as appropriate for entry into a subroutine. */ + ret_addr = make_type (return_address_type); + type_set_return_address (&ret_addr, vfr->PC); + vfy_push_type_t (ret_addr); + merge_into (npc, vfr->current_state); + invalidate_pc (); +} + +static vfy_jclass +construct_primitive_array_type (type_val prim) +{ + vfy_jclass k = NULL; + switch (prim) + { + case boolean_type: + case char_type: + case float_type: + case double_type: + case byte_type: + case short_type: + case int_type: + case long_type: + k = vfy_get_primitive_type ((int) prim); + break; + + /* These aren't used here but we call them out to avoid + warnings. */ + case void_type: + case unsuitable_type: + case return_address_type: + case continuation_type: + case reference_type: + case null_type: + case uninitialized_reference_type: + default: + verify_fail ("unknown type in construct_primitive_array_type"); + } + k = vfy_get_array_class (k); + return k; +} + +/* This pass computes the location of branch targets and also + instruction starts. */ +static void +branch_prepass (void) +{ + int i, pc; + vfr->flags = (char *) vfy_alloc (vfr->current_method->code_length); + + for (i = 0; i < vfr->current_method->code_length; ++i) + vfr->flags[i] = 0; + + vfr->PC = 0; + while (vfr->PC < vfr->current_method->code_length) + { + java_opcode opcode; + /* Set `start_PC' early so that error checking can have the + correct value. */ + vfr->start_PC = vfr->PC; + vfr->flags[vfr->PC] |= FLAG_INSN_START; + + opcode = (java_opcode) vfr->bytecode[vfr->PC++]; + switch (opcode) + { + case op_nop: + case op_aconst_null: + case op_iconst_m1: + case op_iconst_0: + case op_iconst_1: + case op_iconst_2: + case op_iconst_3: + case op_iconst_4: + case op_iconst_5: + case op_lconst_0: + case op_lconst_1: + case op_fconst_0: + case op_fconst_1: + case op_fconst_2: + case op_dconst_0: + case op_dconst_1: + case op_iload_0: + case op_iload_1: + case op_iload_2: + case op_iload_3: + case op_lload_0: + case op_lload_1: + case op_lload_2: + case op_lload_3: + case op_fload_0: + case op_fload_1: + case op_fload_2: + case op_fload_3: + case op_dload_0: + case op_dload_1: + case op_dload_2: + case op_dload_3: + case op_aload_0: + case op_aload_1: + case op_aload_2: + case op_aload_3: + case op_iaload: + case op_laload: + case op_faload: + case op_daload: + case op_aaload: + case op_baload: + case op_caload: + case op_saload: + case op_istore_0: + case op_istore_1: + case op_istore_2: + case op_istore_3: + case op_lstore_0: + case op_lstore_1: + case op_lstore_2: + case op_lstore_3: + case op_fstore_0: + case op_fstore_1: + case op_fstore_2: + case op_fstore_3: + case op_dstore_0: + case op_dstore_1: + case op_dstore_2: + case op_dstore_3: + case op_astore_0: + case op_astore_1: + case op_astore_2: + case op_astore_3: + case op_iastore: + case op_lastore: + case op_fastore: + case op_dastore: + case op_aastore: + case op_bastore: + case op_castore: + case op_sastore: + case op_pop: + case op_pop2: + case op_dup: + case op_dup_x1: + case op_dup_x2: + case op_dup2: + case op_dup2_x1: + case op_dup2_x2: + case op_swap: + case op_iadd: + case op_isub: + case op_imul: + case op_idiv: + case op_irem: + case op_ishl: + case op_ishr: + case op_iushr: + case op_iand: + case op_ior: + case op_ixor: + case op_ladd: + case op_lsub: + case op_lmul: + case op_ldiv: + case op_lrem: + case op_lshl: + case op_lshr: + case op_lushr: + case op_land: + case op_lor: + case op_lxor: + case op_fadd: + case op_fsub: + case op_fmul: + case op_fdiv: + case op_frem: + case op_dadd: + case op_dsub: + case op_dmul: + case op_ddiv: + case op_drem: + case op_ineg: + case op_i2b: + case op_i2c: + case op_i2s: + case op_lneg: + case op_fneg: + case op_dneg: + case op_i2l: + case op_i2f: + case op_i2d: + case op_l2i: + case op_l2f: + case op_l2d: + case op_f2i: + case op_f2l: + case op_f2d: + case op_d2i: + case op_d2l: + case op_d2f: + case op_lcmp: + case op_fcmpl: + case op_fcmpg: + case op_dcmpl: + case op_dcmpg: + case op_monitorenter: + case op_monitorexit: + case op_ireturn: + case op_lreturn: + case op_freturn: + case op_dreturn: + case op_areturn: + case op_return: + case op_athrow: + case op_arraylength: + break; + + case op_bipush: + case op_ldc: + case op_iload: + case op_lload: + case op_fload: + case op_dload: + case op_aload: + case op_istore: + case op_lstore: + case op_fstore: + case op_dstore: + case op_astore: + case op_ret: + case op_newarray: + get_byte (); + break; + + case op_iinc: + case op_sipush: + case op_ldc_w: + case op_ldc2_w: + case op_getstatic: + case op_getfield: + case op_putfield: + case op_putstatic: + case op_new: + case op_anewarray: + case op_instanceof: + case op_checkcast: + case op_invokespecial: + case op_invokestatic: + case op_invokevirtual: + get_short (); + break; + + case op_multianewarray: + get_short (); + get_byte (); + break; + + case op_jsr: + case op_ifeq: + case op_ifne: + case op_iflt: + case op_ifge: + case op_ifgt: + case op_ifle: + case op_if_icmpeq: + case op_if_icmpne: + case op_if_icmplt: + case op_if_icmpge: + case op_if_icmpgt: + case op_if_icmple: + case op_if_acmpeq: + case op_if_acmpne: + case op_ifnull: + case op_ifnonnull: + case op_goto: + note_branch_target (compute_jump (get_short ())); + break; + + case op_tableswitch: + { + jint low, hi; + skip_padding (); + note_branch_target (compute_jump (get_int ())); + low = get_int (); + hi = get_int (); + if (low > hi) + verify_fail_pc ("invalid tableswitch", vfr->start_PC); + for (i = low; i <= hi; ++i) + note_branch_target (compute_jump (get_int ())); + } + break; + + case op_lookupswitch: + { + int npairs; + skip_padding (); + note_branch_target (compute_jump (get_int ())); + npairs = get_int (); + if (npairs < 0) + verify_fail_pc ("too few pairs in lookupswitch", vfr->start_PC); + while (npairs-- > 0) + { + get_int (); + note_branch_target (compute_jump (get_int ())); + } + } + break; + + case op_invokeinterface: + get_short (); + get_byte (); + get_byte (); + break; + + case op_wide: + { + opcode = (java_opcode) get_byte (); + get_short (); + if (opcode == op_iinc) + get_short (); + } + break; + + case op_jsr_w: + case op_goto_w: + note_branch_target (compute_jump (get_int ())); + break; + +#if 0 + /* These are unused here, but we call them out explicitly + so that -Wswitch-enum doesn't complain. */ + case op_putfield_1: + case op_putfield_2: + case op_putfield_4: + case op_putfield_8: + case op_putfield_a: + case op_putstatic_1: + case op_putstatic_2: + case op_putstatic_4: + case op_putstatic_8: + case op_putstatic_a: + case op_getfield_1: + case op_getfield_2s: + case op_getfield_2u: + case op_getfield_4: + case op_getfield_8: + case op_getfield_a: + case op_getstatic_1: + case op_getstatic_2s: + case op_getstatic_2u: + case op_getstatic_4: + case op_getstatic_8: + case op_getstatic_a: +#endif /* VFY_FAST_OPCODES */ + default: + verify_fail_pc ("unrecognized instruction in branch_prepass", + vfr->start_PC); + } + + /* See if any previous branch tried to branch to the middle of + this instruction. */ + for (pc = vfr->start_PC + 1; pc < vfr->PC; ++pc) + { + if ((vfr->flags[pc] & FLAG_BRANCH_TARGET)) + verify_fail_pc ("branch to middle of instruction", pc); + } + } + + /* Verify exception handlers. */ + for (i = 0; i < vfr->current_method->exc_count; ++i) + { + int handler, start, end, htype; + vfy_get_exception (vfr->exception, i, &handler, &start, &end, &htype); + if (! (vfr->flags[handler] & FLAG_INSN_START)) + verify_fail_pc ("exception handler not at instruction start", + handler); + if (! (vfr->flags[start] & FLAG_INSN_START)) + verify_fail_pc ("exception start not at instruction start", start); + if (end != vfr->current_method->code_length + && ! (vfr->flags[end] & FLAG_INSN_START)) + verify_fail_pc ("exception end not at instruction start", end); + + vfr->flags[handler] |= FLAG_BRANCH_TARGET; + } +} + +static void +check_pool_index (int index) +{ + if (index < 0 || index >= vfy_get_constants_size (vfr->current_class)) + verify_fail_pc ("constant pool index out of range", vfr->start_PC); +} + +static type +check_class_constant (int index) +{ + type t; + vfy_constants *pool; + + check_pool_index (index); + pool = vfy_get_constants (vfr->current_class); + if (vfy_tag (pool, index) == JV_CONSTANT_ResolvedClass) + init_type_from_class (&t, vfy_get_pool_class (pool, index)); + else if (vfy_tag (pool, index) == JV_CONSTANT_Class) + init_type_from_string (&t, vfy_get_pool_string (pool, index)); + else + verify_fail_pc ("expected class constant", vfr->start_PC); + return t; +} + +static type +check_constant (int index) +{ + type t; + vfy_constants *pool; + + check_pool_index (index); + pool = vfy_get_constants (vfr->current_class); + if (vfy_tag (pool, index) == JV_CONSTANT_ResolvedString + || vfy_tag (pool, index) == JV_CONSTANT_String) + init_type_from_class (&t, vfy_string_type ()); + else if (vfy_tag (pool, index) == JV_CONSTANT_Integer) + init_type_from_tag (&t, int_type); + else if (vfy_tag (pool, index) == JV_CONSTANT_Float) + init_type_from_tag (&t, float_type); + else + verify_fail_pc ("String, int, or float constant expected", vfr->start_PC); + return t; +} + +static type +check_wide_constant (int index) +{ + type t; + vfy_constants *pool; + + check_pool_index (index); + pool = vfy_get_constants (vfr->current_class); + if (vfy_tag (pool, index) == JV_CONSTANT_Long) + init_type_from_tag (&t, long_type); + else if (vfy_tag (pool, index) == JV_CONSTANT_Double) + init_type_from_tag (&t, double_type); + else + verify_fail_pc ("long or double constant expected", vfr->start_PC); + return t; +} + +/* Helper for both field and method. These are laid out the same in + the constant pool. */ +static type +handle_field_or_method (int index, int expected, + vfy_string *name, vfy_string *fmtype) +{ + vfy_uint_16 class_index, name_and_type_index; + vfy_uint_16 name_index, desc_index; + vfy_constants *pool; + + check_pool_index (index); + pool = vfy_get_constants (vfr->current_class); + if (vfy_tag (pool, index) != expected) + verify_fail_pc ("didn't see expected constant", vfr->start_PC); + /* Once we know we have a Fieldref or Methodref we assume that it + is correctly laid out in the constant pool. I think the code + in defineclass.cc guarantees this. */ + vfy_load_indexes (pool, index, &class_index, &name_and_type_index); + vfy_load_indexes (pool, name_and_type_index, &name_index, &desc_index); + + *name = vfy_get_pool_string (pool, name_index); + *fmtype = vfy_get_pool_string (pool, desc_index); + + return check_class_constant (class_index); +} + +/* Return field's type, compute class' type if requested. */ +static type +check_field_constant (int index, type *class_type) +{ + vfy_string name, field_type; + const char *typec; + int len; + type t; + + type ct = handle_field_or_method (index, + JV_CONSTANT_Fieldref, + &name, &field_type); + if (class_type) + *class_type = ct; + typec = vfy_string_bytes (field_type); + len = vfy_string_length (field_type); + if (typec[0] == '[' || typec[0] == 'L') + init_type_from_string (&t, field_type); + else + init_type_from_tag (&t, get_type_val_for_signature (typec[0])); + return t; +} + +static type +check_method_constant (int index, bool is_interface, + vfy_string *method_name, + vfy_string *method_signature) +{ + return handle_field_or_method (index, + (is_interface + ? JV_CONSTANT_InterfaceMethodref + : JV_CONSTANT_Methodref), + method_name, method_signature); +} + +static char * +get_one_type (char *p, type *t) +{ + const char *start = p; + vfy_jclass k; + type_val rt; + char v; + + int arraycount = 0; + while (*p == '[') + { + ++arraycount; + ++p; + } + + v = *p++; + + if (v == 'L') + { + vfy_string name; + while (*p != ';') + ++p; + ++p; + name = vfy_get_string (start, p - start); + *t = make_type_from_string (name); + return p; + } + + /* Casting to jchar here is ok since we are looking at an ASCII + character. */ + rt = get_type_val_for_signature (v); + + if (arraycount == 0) + { + /* Callers of this function eventually push their arguments on + the stack. So, promote them here. */ + type new_t = make_type (rt); + vfy_promote_type (&new_t); + *t = new_t; + return p; + } + + k = construct_primitive_array_type (rt); + while (--arraycount > 0) + k = vfy_get_array_class (k); + *t = make_type_from_class (k); + return p; +} + +static void +compute_argument_types (vfy_string signature, type *types) +{ + int i; + char *p = (char *) vfy_string_bytes (signature); + + /* Skip `('. */ + ++p; + + i = 0; + while (*p != ')') + p = get_one_type (p, &types[i++]); +} + +static type +compute_return_type (vfy_string signature) +{ + char *p = (char *) vfy_string_bytes (signature); + type t; + while (*p != ')') + ++p; + ++p; + get_one_type (p, &t); + return t; +} + +static void +check_return_type (type onstack) +{ + type rt = compute_return_type (vfy_get_signature (vfr->current_method)); + if (! types_compatible (&rt, &onstack)) + verify_fail ("incompatible return type"); +} + +/* Initialize the stack for the new method. Returns true if this + method is an instance initializer. */ +static bool +initialize_stack (void) +{ + int arg_count, i; + int var = 0; + bool is_init = vfy_strings_equal (vfy_get_method_name (vfr->current_method), + vfy_init_name()); + bool is_clinit = vfy_strings_equal (vfy_get_method_name (vfr->current_method), + vfy_clinit_name()); + + if (! vfy_is_static (vfr->current_method)) + { + type kurr = make_type_from_class (vfr->current_class); + if (is_init) + { + type_set_uninitialized (&kurr, SELF); + is_init = true; + } + else if (is_clinit) + verify_fail ("<clinit> method must be static"); + set_variable (0, kurr); + state_set_this_type (vfr->current_state, &kurr); + ++var; + } + else + { + if (is_init) + verify_fail ("<init> method must be non-static"); + } + + /* We have to handle wide arguments specially here. */ + arg_count = vfy_count_arguments (vfy_get_signature (vfr->current_method)); + { + type arg_types[arg_count]; + compute_argument_types (vfy_get_signature (vfr->current_method), arg_types); + for (i = 0; i < arg_count; ++i) + { + set_variable (var, arg_types[i]); + ++var; + if (type_iswide (&arg_types[i])) + ++var; + } + } + + return is_init; +} + +static void +verify_instructions_0 (void) +{ + int i; + bool this_is_init; + + vfr->current_state = make_state (vfr->current_method->max_stack, + vfr->current_method->max_locals); + + vfr->PC = 0; + vfr->start_PC = 0; + + /* True if we are verifying an instance initializer. */ + this_is_init = initialize_stack (); + + vfr->states = (state_list **) vfy_alloc (sizeof (state_list *) + * vfr->current_method->code_length); + + for (i = 0; i < vfr->current_method->code_length; ++i) + vfr->states[i] = NULL; + + vfr->next_verify_state = NULL; + + while (true) + { + java_opcode opcode; + + /* If the PC was invalidated, get a new one from the work list. */ + if (vfr->PC == NO_NEXT) + { + state *new_state = pop_jump (); + /* If it is null, we're done. */ + if (new_state == NULL) + break; + + vfr->PC = state_get_pc (new_state); + debug_print ("== State pop from pending list\n"); + /* Set up the current state. */ + copy_state (vfr->current_state, new_state, + vfr->current_method->max_stack, vfr->current_method->max_locals); + } + else + { + /* We only have to do this checking in the situation where + control flow falls through from the previous + instruction. Otherwise merging is done at the time we + push the branch. */ + if (vfr->states[vfr->PC] != NULL) + { + /* We've already visited this instruction. So merge + the states together. It is simplest, but not most + efficient, to just always invalidate the PC here. */ + merge_into (vfr->PC, vfr->current_state); + invalidate_pc (); + continue; + } + } + + /* Control can't fall off the end of the bytecode. We need to + check this in both cases, not just the fall-through case, + because we don't check to see whether a `jsr' appears at + the end of the bytecode until we process a `ret'. */ + if (vfr->PC >= vfr->current_method->code_length) + verify_fail ("fell off end"); + vfr->flags[vfr->PC] |= FLAG_INSN_SEEN; + + /* We only have to keep saved state at branch targets. If + we're at a branch target and the state here hasn't been set + yet, we set it now. You might notice that `ret' targets + won't necessarily have FLAG_BRANCH_TARGET set. This + doesn't matter, since those states will be filled in by + merge_into. */ + /* Note that other parts of the compiler assume that there is a + label with a type map at PC=0. */ + if (vfr->states[vfr->PC] == NULL + && (vfr->PC == 0 || (vfr->flags[vfr->PC] & FLAG_BRANCH_TARGET) != 0)) + add_new_state (vfr->PC, vfr->current_state); + + /* Set this before handling exceptions so that debug output is + sane. */ + vfr->start_PC = vfr->PC; + + /* Update states for all active exception handlers. Ordinarily + there are not many exception handlers. So we simply run + through them all. */ + for (i = 0; i < vfr->current_method->exc_count; ++i) + { + int hpc, start, end, htype; + vfy_get_exception (vfr->exception, i, &hpc, &start, &end, &htype); + if (vfr->PC >= start && vfr->PC < end) + { + type handler = make_type_from_class (vfy_throwable_type ()); + if (htype != 0) + handler = check_class_constant (htype); + push_exception_jump (handler, hpc); + } + } + + + debug_print_state (vfr->current_state, " ", vfr->PC, + vfr->current_method->max_stack, + vfr->current_method->max_locals); + opcode = (java_opcode) vfr->bytecode[vfr->PC++]; + switch (opcode) + { + case op_nop: + break; + + case op_aconst_null: + push_type (null_type); + break; + + case op_iconst_m1: + case op_iconst_0: + case op_iconst_1: + case op_iconst_2: + case op_iconst_3: + case op_iconst_4: + case op_iconst_5: + push_type (int_type); + break; + + case op_lconst_0: + case op_lconst_1: + push_type (long_type); + break; + + case op_fconst_0: + case op_fconst_1: + case op_fconst_2: + push_type (float_type); + break; + + case op_dconst_0: + case op_dconst_1: + push_type (double_type); + break; + + case op_bipush: + get_byte (); + push_type (int_type); + break; + + case op_sipush: + get_short (); + push_type (int_type); + break; + + case op_ldc: + push_type_t (check_constant (get_byte ())); + break; + case op_ldc_w: + push_type_t (check_constant (get_ushort ())); + break; + case op_ldc2_w: + push_type_t (check_wide_constant (get_ushort ())); + break; + + case op_iload: + push_type_t (get_variable (get_byte (), int_type)); + break; + case op_lload: + push_type_t (get_variable (get_byte (), long_type)); + break; + case op_fload: + push_type_t (get_variable (get_byte (), float_type)); + break; + case op_dload: + push_type_t (get_variable (get_byte (), double_type)); + break; + case op_aload: + push_type_t (get_variable (get_byte (), reference_type)); + break; + + case op_iload_0: + case op_iload_1: + case op_iload_2: + case op_iload_3: + push_type_t (get_variable (opcode - op_iload_0, int_type)); + break; + case op_lload_0: + case op_lload_1: + case op_lload_2: + case op_lload_3: + push_type_t (get_variable (opcode - op_lload_0, long_type)); + break; + case op_fload_0: + case op_fload_1: + case op_fload_2: + case op_fload_3: + push_type_t (get_variable (opcode - op_fload_0, float_type)); + break; + case op_dload_0: + case op_dload_1: + case op_dload_2: + case op_dload_3: + push_type_t (get_variable (opcode - op_dload_0, double_type)); + break; + case op_aload_0: + case op_aload_1: + case op_aload_2: + case op_aload_3: + push_type_t (get_variable (opcode - op_aload_0, reference_type)); + break; + case op_iaload: + pop_type (int_type); + push_type_t (require_array_type (pop_init_ref (reference_type), + int_type)); + break; + case op_laload: + pop_type (int_type); + push_type_t (require_array_type (pop_init_ref (reference_type), + long_type)); + break; + case op_faload: + pop_type (int_type); + push_type_t (require_array_type (pop_init_ref (reference_type), + float_type)); + break; + case op_daload: + pop_type (int_type); + push_type_t (require_array_type (pop_init_ref (reference_type), + double_type)); + break; + case op_aaload: + pop_type (int_type); + push_type_t (require_array_type (pop_init_ref (reference_type), + reference_type)); + break; + case op_baload: + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), byte_type); + push_type (int_type); + break; + case op_caload: + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), char_type); + push_type (int_type); + break; + case op_saload: + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), short_type); + push_type (int_type); + break; + case op_istore: + set_variable (get_byte (), pop_type (int_type)); + break; + case op_lstore: + set_variable (get_byte (), pop_type (long_type)); + break; + case op_fstore: + set_variable (get_byte (), pop_type (float_type)); + break; + case op_dstore: + set_variable (get_byte (), pop_type (double_type)); + break; + case op_astore: + set_variable (get_byte (), pop_ref_or_return ()); + break; + case op_istore_0: + case op_istore_1: + case op_istore_2: + case op_istore_3: + set_variable (opcode - op_istore_0, pop_type (int_type)); + break; + case op_lstore_0: + case op_lstore_1: + case op_lstore_2: + case op_lstore_3: + set_variable (opcode - op_lstore_0, pop_type (long_type)); + break; + case op_fstore_0: + case op_fstore_1: + case op_fstore_2: + case op_fstore_3: + set_variable (opcode - op_fstore_0, pop_type (float_type)); + break; + case op_dstore_0: + case op_dstore_1: + case op_dstore_2: + case op_dstore_3: + set_variable (opcode - op_dstore_0, pop_type (double_type)); + break; + case op_astore_0: + case op_astore_1: + case op_astore_2: + case op_astore_3: + set_variable (opcode - op_astore_0, pop_ref_or_return ()); + break; + case op_iastore: + pop_type (int_type); + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), int_type); + break; + case op_lastore: + pop_type (long_type); + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), long_type); + break; + case op_fastore: + pop_type (float_type); + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), float_type); + break; + case op_dastore: + pop_type (double_type); + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), double_type); + break; + case op_aastore: + pop_type (reference_type); + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), reference_type); + break; + case op_bastore: + pop_type (int_type); + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), byte_type); + break; + case op_castore: + pop_type (int_type); + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), char_type); + break; + case op_sastore: + pop_type (int_type); + pop_type (int_type); + require_array_type (pop_init_ref (reference_type), short_type); + break; + case op_pop: + pop32 (); + break; + case op_pop2: + { + type t = pop_raw (); + if (! type_iswide (&t)) + pop32 (); + } + break; + case op_dup: + { + type t = pop32 (); + push_type_t (t); + push_type_t (t); + } + break; + case op_dup_x1: + { + type t1 = pop32 (); + type t2 = pop32 (); + push_type_t (t1); + push_type_t (t2); + push_type_t (t1); + } + break; + case op_dup_x2: + { + type t1 = pop32 (); + type t2 = pop_raw (); + if (! type_iswide (&t2)) + { + type t3 = pop32 (); + push_type_t (t1); + push_type_t (t3); + } + else + push_type_t (t1); + push_type_t (t2); + push_type_t (t1); + } + break; + case op_dup2: + { + type t = pop_raw (); + if (! type_iswide (&t)) + { + type t2 = pop32 (); + push_type_t (t2); + push_type_t (t); + push_type_t (t2); + } + else + push_type_t (t); + push_type_t (t); + } + break; + case op_dup2_x1: + { + type t1 = pop_raw (); + type t2 = pop32 (); + if (! type_iswide (&t1)) + { + type t3 = pop32 (); + push_type_t (t2); + push_type_t (t1); + push_type_t (t3); + } + else + push_type_t (t1); + push_type_t (t2); + push_type_t (t1); + } + break; + case op_dup2_x2: + { + type t1 = pop_raw (); + if (type_iswide (&t1)) + { + type t2 = pop_raw (); + if (type_iswide (&t2)) + { + push_type_t (t1); + push_type_t (t2); + } + else + { + type t3 = pop32 (); + push_type_t (t1); + push_type_t (t3); + push_type_t (t2); + } + push_type_t (t1); + } + else + { + type t2 = pop32 (); + type t3 = pop_raw (); + if (type_iswide (&t3)) + { + push_type_t (t2); + push_type_t (t1); + } + else + { + type t4 = pop32 (); + push_type_t (t2); + push_type_t (t1); + push_type_t (t4); + } + push_type_t (t3); + push_type_t (t2); + push_type_t (t1); + } + } + break; + case op_swap: + { + type t1 = pop32 (); + type t2 = pop32 (); + push_type_t (t1); + push_type_t (t2); + } + break; + case op_iadd: + case op_isub: + case op_imul: + case op_idiv: + case op_irem: + case op_ishl: + case op_ishr: + case op_iushr: + case op_iand: + case op_ior: + case op_ixor: + pop_type (int_type); + push_type_t (pop_type (int_type)); + break; + case op_ladd: + case op_lsub: + case op_lmul: + case op_ldiv: + case op_lrem: + case op_land: + case op_lor: + case op_lxor: + pop_type (long_type); + push_type_t (pop_type (long_type)); + break; + case op_lshl: + case op_lshr: + case op_lushr: + pop_type (int_type); + push_type_t (pop_type (long_type)); + break; + case op_fadd: + case op_fsub: + case op_fmul: + case op_fdiv: + case op_frem: + pop_type (float_type); + push_type_t (pop_type (float_type)); + break; + case op_dadd: + case op_dsub: + case op_dmul: + case op_ddiv: + case op_drem: + pop_type (double_type); + push_type_t (pop_type (double_type)); + break; + case op_ineg: + case op_i2b: + case op_i2c: + case op_i2s: + push_type_t (pop_type (int_type)); + break; + case op_lneg: + push_type_t (pop_type (long_type)); + break; + case op_fneg: + push_type_t (pop_type (float_type)); + break; + case op_dneg: + push_type_t (pop_type (double_type)); + break; + case op_iinc: + get_variable (get_byte (), int_type); + get_byte (); + break; + case op_i2l: + pop_type (int_type); + push_type (long_type); + break; + case op_i2f: + pop_type (int_type); + push_type (float_type); + break; + case op_i2d: + pop_type (int_type); + push_type (double_type); + break; + case op_l2i: + pop_type (long_type); + push_type (int_type); + break; + case op_l2f: + pop_type (long_type); + push_type (float_type); + break; + case op_l2d: + pop_type (long_type); + push_type (double_type); + break; + case op_f2i: + pop_type (float_type); + push_type (int_type); + break; + case op_f2l: + pop_type (float_type); + push_type (long_type); + break; + case op_f2d: + pop_type (float_type); + push_type (double_type); + break; + case op_d2i: + pop_type (double_type); + push_type (int_type); + break; + case op_d2l: + pop_type (double_type); + push_type (long_type); + break; + case op_d2f: + pop_type (double_type); + push_type (float_type); + break; + case op_lcmp: + pop_type (long_type); + pop_type (long_type); + push_type (int_type); + break; + case op_fcmpl: + case op_fcmpg: + pop_type (float_type); + pop_type (float_type); + push_type (int_type); + break; + case op_dcmpl: + case op_dcmpg: + pop_type (double_type); + pop_type (double_type); + push_type (int_type); + break; + case op_ifeq: + case op_ifne: + case op_iflt: + case op_ifge: + case op_ifgt: + case op_ifle: + pop_type (int_type); + push_jump (get_short ()); + break; + case op_if_icmpeq: + case op_if_icmpne: + case op_if_icmplt: + case op_if_icmpge: + case op_if_icmpgt: + case op_if_icmple: + pop_type (int_type); + pop_type (int_type); + push_jump (get_short ()); + break; + case op_if_acmpeq: + case op_if_acmpne: + pop_type (reference_type); + pop_type (reference_type); + push_jump (get_short ()); + break; + case op_goto: + push_jump (get_short ()); + invalidate_pc (); + break; + case op_jsr: + handle_jsr_insn (get_short ()); + break; + case op_ret: + handle_ret_insn (get_byte ()); + break; + case op_tableswitch: + { + int i; + jint low, high; + pop_type (int_type); + skip_padding (); + push_jump (get_int ()); + low = get_int (); + high = get_int (); + /* Already checked LOW -vs- HIGH. */ + for (i = low; i <= high; ++i) + push_jump (get_int ()); + invalidate_pc (); + } + break; + + case op_lookupswitch: + { + int i; + jint npairs, lastkey; + + pop_type (int_type); + skip_padding (); + push_jump (get_int ()); + npairs = get_int (); + /* Already checked NPAIRS >= 0. */ + lastkey = 0; + for (i = 0; i < npairs; ++i) + { + jint key = get_int (); + if (i > 0 && key <= lastkey) + verify_fail_pc ("lookupswitch pairs unsorted", vfr->start_PC); + lastkey = key; + push_jump (get_int ()); + } + invalidate_pc (); + } + break; + case op_ireturn: + check_return_type (pop_type (int_type)); + invalidate_pc (); + break; + case op_lreturn: + check_return_type (pop_type (long_type)); + invalidate_pc (); + break; + case op_freturn: + check_return_type (pop_type (float_type)); + invalidate_pc (); + break; + case op_dreturn: + check_return_type (pop_type (double_type)); + invalidate_pc (); + break; + case op_areturn: + check_return_type (pop_init_ref (reference_type)); + invalidate_pc (); + break; + case op_return: + /* We only need to check this when the return type is + void, because all instance initializers return void. */ + if (this_is_init) + state_check_this_initialized (vfr->current_state); + check_return_type (make_type (void_type)); + invalidate_pc (); + break; + case op_getstatic: + push_type_t (check_field_constant (get_ushort (), NULL)); + break; + case op_putstatic: + pop_type_t (check_field_constant (get_ushort (), NULL)); + break; + case op_getfield: + { + type klass; + type field = check_field_constant (get_ushort (), &klass); + pop_type_t (klass); + push_type_t (field); + } + break; + case op_putfield: + { + type klass; + type field = check_field_constant (get_ushort (), &klass); + pop_type_t (field); + + /* We have an obscure special case here: we can use + `putfield' on a field declared in this class, even if + `this' has not yet been initialized. */ + if (! type_initialized (&vfr->current_state->this_type) + && vfr->current_state->this_type.pc == SELF) + type_set_uninitialized (&klass, SELF); + pop_type_t (klass); + } + break; + + case op_invokevirtual: + case op_invokespecial: + case op_invokestatic: + case op_invokeinterface: + { + vfy_string method_name, method_signature; + const char *namec; + int i, arg_count; + type rt; + bool is_init = false; + + type class_type + = check_method_constant (get_ushort (), + opcode == op_invokeinterface, + &method_name, + &method_signature); + /* NARGS is only used when we're processing + invokeinterface. It is simplest for us to compute it + here and then verify it later. */ + int nargs = 0; + if (opcode == op_invokeinterface) + { + nargs = get_byte (); + if (get_byte () != 0) + verify_fail ("invokeinterface dummy byte is wrong"); + } + + namec = vfy_string_bytes (method_name); + + if (vfy_strings_equal (method_name, vfy_init_name())) + { + is_init = true; + if (opcode != op_invokespecial) + verify_fail ("can't invoke <init>"); + } + else if (namec[0] == '<') + verify_fail ("can't invoke method starting with `<'"); + + arg_count = vfy_count_arguments (method_signature); + { + /* Pop arguments and check types. */ + type arg_types[arg_count]; + + compute_argument_types (method_signature, arg_types); + for (i = arg_count - 1; i >= 0; --i) + { + /* This is only used for verifying the byte for + invokeinterface. */ + nargs -= type_depth (&arg_types[i]); + pop_init_ref_t (arg_types[i]); + } + } + + if (opcode == op_invokeinterface + && nargs != 1) + verify_fail ("wrong argument count for invokeinterface"); + + if (opcode != op_invokestatic) + { + type raw; + type t = class_type; + if (is_init) + { + /* In this case the PC doesn't matter. */ + type_set_uninitialized (&t, UNINIT); + /* FIXME: check to make sure that the <init> + call is to the right class. + It must either be super or an exact class + match. */ + } + raw = pop_raw (); + if (! types_compatible (&t, &raw)) + verify_fail ("incompatible type on stack"); + + if (is_init) + state_set_initialized (vfr->current_state, + type_get_pc (&raw), vfr->current_method->max_locals); + } + + rt = compute_return_type (method_signature); + if (! type_isvoid (&rt)) + push_type_t (rt); + } + break; + + case op_new: + { + type t = check_class_constant (get_ushort ()); + if (type_isarray (&t) || type_isinterface (&t) + || type_isabstract (&t)) + verify_fail ("type is array, interface, or abstract"); + type_set_uninitialized (&t, vfr->start_PC); + push_type_t (t); + } + break; + + case op_newarray: + { + int atype = get_byte (); + type t; + /* We intentionally have chosen constants to make this + valid. */ + if (atype < boolean_type || atype > long_type) + verify_fail_pc ("type not primitive", vfr->start_PC); + pop_type (int_type); + init_type_from_class (&t, construct_primitive_array_type (atype)); + push_type_t (t); + } + break; + case op_anewarray: + { + type t; + pop_type (int_type); + t = check_class_constant (get_ushort ()); + push_type_t (type_to_array (&t)); + } + break; + case op_arraylength: + { + type t = pop_init_ref (reference_type); + if (! type_isarray (&t) && ! type_isnull (&t)) + verify_fail ("array type expected"); + push_type (int_type); + } + break; + case op_athrow: + pop_type_t (make_type_from_class (vfy_throwable_type ())); + invalidate_pc (); + break; + case op_checkcast: + pop_init_ref (reference_type); + push_type_t (check_class_constant (get_ushort ())); + break; + case op_instanceof: + pop_init_ref (reference_type); + check_class_constant (get_ushort ()); + push_type (int_type); + break; + case op_monitorenter: + pop_init_ref (reference_type); + break; + case op_monitorexit: + pop_init_ref (reference_type); + break; + case op_wide: + { + switch (get_byte ()) + { + case op_iload: + push_type_t (get_variable (get_ushort (), int_type)); + break; + case op_lload: + push_type_t (get_variable (get_ushort (), long_type)); + break; + case op_fload: + push_type_t (get_variable (get_ushort (), float_type)); + break; + case op_dload: + push_type_t (get_variable (get_ushort (), double_type)); + break; + case op_aload: + push_type_t (get_variable (get_ushort (), reference_type)); + break; + case op_istore: + set_variable (get_ushort (), pop_type (int_type)); + break; + case op_lstore: + set_variable (get_ushort (), pop_type (long_type)); + break; + case op_fstore: + set_variable (get_ushort (), pop_type (float_type)); + break; + case op_dstore: + set_variable (get_ushort (), pop_type (double_type)); + break; + case op_astore: + set_variable (get_ushort (), pop_init_ref (reference_type)); + break; + case op_ret: + handle_ret_insn (get_short ()); + break; + case op_iinc: + get_variable (get_ushort (), int_type); + get_short (); + break; + default: + verify_fail_pc ("unrecognized wide instruction", vfr->start_PC); + } + } + break; + case op_multianewarray: + { + int i; + type atype = check_class_constant (get_ushort ()); + int dim = get_byte (); + if (dim < 1) + verify_fail_pc ("too few dimensions to multianewarray", vfr->start_PC); + type_verify_dimensions (&atype, dim); + for (i = 0; i < dim; ++i) + pop_type (int_type); + push_type_t (atype); + } + break; + case op_ifnull: + case op_ifnonnull: + pop_type (reference_type); + push_jump (get_short ()); + break; + case op_goto_w: + push_jump (get_int ()); + invalidate_pc (); + break; + case op_jsr_w: + handle_jsr_insn (get_int ()); + break; + +#if 0 + /* These are unused here, but we call them out explicitly + so that -Wswitch-enum doesn't complain. */ + case op_putfield_1: + case op_putfield_2: + case op_putfield_4: + case op_putfield_8: + case op_putfield_a: + case op_putstatic_1: + case op_putstatic_2: + case op_putstatic_4: + case op_putstatic_8: + case op_putstatic_a: + case op_getfield_1: + case op_getfield_2s: + case op_getfield_2u: + case op_getfield_4: + case op_getfield_8: + case op_getfield_a: + case op_getstatic_1: + case op_getstatic_2s: + case op_getstatic_2u: + case op_getstatic_4: + case op_getstatic_8: + case op_getstatic_a: +#endif + default: + /* Unrecognized opcode. */ + verify_fail_pc ("unrecognized instruction in verify_instructions_0", + vfr->start_PC); + } + } +} + +/* This turns a `type' into something suitable for use by the type map + in the other parts of the compiler. In particular, reference types + are mapped to Object, primitive types are unchanged, and other + types are mapped using special functions declared in verify.h. */ +static vfy_jclass +collapse_type (type *t) +{ + switch (t->key) + { + case void_type: + case boolean_type: + case char_type: + case float_type: + case double_type: + case byte_type: + case short_type: + case int_type: + case long_type: + return vfy_get_primitive_type (t->key); + + case unsuitable_type: + case continuation_type: + return vfy_unsuitable_type (); + + case return_address_type: + return vfy_return_address_type (); + + case null_type: + return vfy_null_type (); + + case reference_type: + case uninitialized_reference_type: + return vfy_object_type (); + } + + abort (); +} + +static void +verify_instructions (void) +{ + int i; + + branch_prepass (); + verify_instructions_0 (); + + /* Now tell the rest of the compiler about the types we've found. */ + for (i = 0; i < vfr->current_method->code_length; ++i) + { + int j, slot; + struct state *curr; + + if ((vfr->flags[i] & FLAG_INSN_SEEN) != 0) + vfy_note_instruction_seen (i); + + if (! vfr->states[i]) + continue; + + curr = vfr->states[i]->val; + vfy_note_stack_depth (vfr->current_method, i, curr->stackdepth); + + /* Tell the compiler about each local variable. */ + for (j = 0; j < vfr->current_method->max_locals; ++j) + vfy_note_local_type (vfr->current_method, i, j, + collapse_type (&curr->locals[j])); + /* Tell the compiler about each stack slot. */ + for (slot = j = 0; j < curr->stacktop; ++j, ++slot) + { + vfy_note_stack_type (vfr->current_method, i, slot, + collapse_type (&curr->stack[j])); + if (type_iswide (&curr->stack[j])) + { + ++slot; + vfy_note_stack_type (vfr->current_method, i, slot, + vfy_unsuitable_type ()); + } + } + if (slot != curr->stackdepth) + abort (); + } +} + +#if 0 +_Jv_BytecodeVerifier (_Jv_InterpMethod *m) +{ + /* We just print the text as utf-8. This is just for debugging + anyway. */ + debug_print ("--------------------------------\n"); + debug_print ("-- Verifying method `%s'\n", m->self->name->chars()); + +} +#endif + +static void +make_verifier_context (vfy_method *m) +{ + vfr = (verifier_context *) vfy_alloc (sizeof (struct verifier_context)); + + vfr->current_method = m; + vfr->bytecode = vfy_get_bytecode (m); + vfr->exception = vfy_get_exceptions (m); + vfr->current_class = m->defining_class; + + vfr->states = NULL; + vfr->flags = NULL; + vfr->utf8_list = NULL; + vfr->isect_list = NULL; +} + +static void +free_verifier_context (void) +{ + vfy_string_list *utf8_list; + ref_intersection *isect_list; + + if (vfr->flags) + vfy_free (vfr->flags); + + utf8_list = vfr->utf8_list; + while (utf8_list != NULL) + { + vfy_string_list *n = utf8_list->next; + vfy_free (utf8_list); + utf8_list = n; + } + + isect_list = vfr->isect_list; + while (isect_list != NULL) + { + ref_intersection *next = isect_list->alloc_next; + vfy_free (isect_list); + isect_list = next; + } + + if (vfr->states != NULL) + { + int i; + for (i = 0; i < vfr->current_method->code_length; ++i) + { + state_list *iter = vfr->states[i]; + while (iter != NULL) + { + state_list *next = iter->next; + vfy_free (iter->val); + vfy_free (iter); + iter = next; + } + } + vfy_free (vfr->states); + } + + vfy_free (vfr); +} + +int +verify_method (vfy_method *meth) +{ + debug_print ("verify_method (%s) %i\n", vfy_string_bytes (meth->name), + meth->code_length); + + if (vfr != NULL) + verify_fail ("verifier re-entered"); + + make_verifier_context (meth); + verify_instructions (); + free_verifier_context (); + vfr = NULL; + + return 1; +} diff --git a/gcc/java/verify.c b/gcc/java/verify.c index aaf7e4d..8eb0c30 100644 --- a/gcc/java/verify.c +++ b/gcc/java/verify.c @@ -89,23 +89,23 @@ check_pending_block (tree target_label) if (current_subr == NULL_TREE) { - if (LABEL_IN_SUBR (target_label)) - return "might transfer control into subroutine"; +/* if (LABEL_IN_SUBR (target_label)) */ +/* return "might transfer control into subroutine"; */ } else { if (LABEL_IN_SUBR (target_label)) { - if (LABEL_SUBR_START (target_label) != current_subr) - return "transfer out of subroutine"; +/* if (LABEL_SUBR_START (target_label) != current_subr) */ +/* return "transfer out of subroutine"; */ } else if (! LABEL_VERIFIED (target_label)) { LABEL_IN_SUBR (target_label) = 1; LABEL_SUBR_START (target_label) = current_subr; } - else - return "transfer out of subroutine"; +/* else */ +/* return "transfer out of subroutine"; */ } return NULL; } @@ -126,6 +126,54 @@ subroutine_nesting (tree label) return nesting; } +static tree +defer_merging (tree type1, tree type2) +{ + // FIXME: This is just a placeholder until we replace the verifier + // altogether. We really need to ouput a type assertion for all of + // the types, every time they are used. + return object_ptr_type_node; + + if (TREE_CODE (type1) == POINTER_TYPE) + type1 = TREE_TYPE (type1); + if (TREE_CODE (type2) == POINTER_TYPE) + type2 = TREE_TYPE (type2); + + if (TREE_CODE (type1) == RECORD_TYPE && TREE_CODE (type2) == RECORD_TYPE) + { + tree list = build_tree_list (type1, NULL_TREE); + list = tree_cons (type2, NULL_TREE, list); + return list; + } + + if (TREE_CODE (type1) == TREE_LIST && TREE_CODE (type2) == TREE_LIST) + { + return chainon (copy_list (type1), copy_list (type2)); + } + + if (TREE_CODE (type1) == TREE_LIST && TREE_CODE (type2) == RECORD_TYPE) + { + tree tmp = type1; + do + { + if (TREE_PURPOSE (tmp) == type2) + return type1; + tmp = TREE_CHAIN (tmp); + } + while (tmp); + + return tree_cons (type2, NULL_TREE, copy_list (type1)); + } + + if (TREE_CODE (type2) == TREE_LIST && TREE_CODE (type1) == RECORD_TYPE) + { + return defer_merging (type2, type1); + } + + abort (); +} + + /* Return the "merged" types of TYPE1 and TYPE2. If either is primitive, the other must match (after promotion to int). For reference types, return the common super-class. @@ -138,7 +186,11 @@ merge_types (tree type1, tree type2) return type1; if (type1 == TYPE_UNKNOWN || type2 == TYPE_UNKNOWN || type1 == TYPE_RETURN_ADDR || type2 == TYPE_RETURN_ADDR) - return TYPE_UNKNOWN; + return TYPE_UNKNOWN; + + if (TREE_CODE (type1) == TREE_LIST || TREE_CODE (type2) == TREE_LIST) + return defer_merging (type1, type2); + if (TREE_CODE (type1) == POINTER_TYPE && TREE_CODE (type2) == POINTER_TYPE) { int depth1, depth2; @@ -153,6 +205,9 @@ merge_types (tree type1, tree type2) tt1 = TREE_TYPE (type1); tt2 = TREE_TYPE (type2); + if (TYPE_DUMMY (tt1) || TYPE_DUMMY (tt2)) + return defer_merging (tt1, tt2); + /* If tt{1,2} haven't been properly loaded, now is a good time to do it. */ if (!TYPE_SIZE (tt1)) @@ -193,31 +248,10 @@ merge_types (tree type1, tree type2) return object_ptr_type_node; } - if (CLASS_INTERFACE (TYPE_NAME (tt1))) - { - /* FIXME: should see if two interfaces have a common - superinterface. */ - if (CLASS_INTERFACE (TYPE_NAME (tt2))) - { - /* This is a kludge, but matches what Sun's verifier does. - It can be tricked, but is safe as long as type errors - (i.e. interface method calls) are caught at run-time. */ - return object_ptr_type_node; - } - else - { - if (can_widen_reference_to (tt2, tt1)) - return type1; - else - return object_ptr_type_node; - } - } - else if (CLASS_INTERFACE (TYPE_NAME (tt2))) + if (CLASS_INTERFACE (TYPE_NAME (tt1)) + || (CLASS_INTERFACE (TYPE_NAME (tt2)))) { - if (can_widen_reference_to (tt1, tt2)) - return type2; - else - return object_ptr_type_node; + return object_ptr_type_node; } type1 = tt1; @@ -675,6 +709,8 @@ verify_jvm_instructions (JCF* jcf, const unsigned char *byte_ops, long length) VERIFICATION_ERROR_WITH_INDEX ("invalid local variable index %d in load"); tmp = type_map[index]; + if (TREE_CODE (tmp) != TREE_LIST) + { if (tmp == TYPE_UNKNOWN) VERIFICATION_ERROR_WITH_INDEX ("loading local variable %d which has unknown type"); @@ -688,6 +724,7 @@ verify_jvm_instructions (JCF* jcf, const unsigned char *byte_ops, long length) : type != tmp)) VERIFICATION_ERROR_WITH_INDEX ("loading local variable %d which has invalid type"); + } PUSH_TYPE (tmp); goto note_used; case OPCODE_istore: type = int_type_node; goto general_store; @@ -736,7 +773,7 @@ verify_jvm_instructions (JCF* jcf, const unsigned char *byte_ops, long length) prev_eh_ranges = NULL_EH_RANGE; /* Allocate decl for this variable now, so we get a temporary -! that survives the whole method. */ + that survives the whole method. */ find_local_variable (index, type, oldpc); if (TYPE_IS_WIDE (type)) @@ -1092,6 +1129,10 @@ verify_jvm_instructions (JCF* jcf, const unsigned char *byte_ops, long length) if (! CLASS_LOADED_P (self_type)) load_class (self_type, 1); + if (TYPE_DUMMY (self_type) && op_code == OPCODE_invokeinterface) + /* Assume we are an interface. */ + CLASS_INTERFACE (TYPE_NAME (self_type)) = 1; + self_is_interface = CLASS_INTERFACE (TYPE_NAME (self_type)); method_name = COMPONENT_REF_NAME (¤t_jcf->cpool, index); method_type = parse_signature_string ((const unsigned char *) IDENTIFIER_POINTER (sig), @@ -1129,7 +1170,6 @@ verify_jvm_instructions (JCF* jcf, const unsigned char *byte_ops, long length) if (!nargs || notZero) VERIFICATION_ERROR ("invalid argument number in invokeinterface"); - /* If we verify/resolve the constant pool, as we should, this test (and the one just following) are redundant. */ if (! self_is_interface) diff --git a/gcc/java/verify.h b/gcc/java/verify.h new file mode 100644 index 0000000..6657ffc --- /dev/null +++ b/gcc/java/verify.h @@ -0,0 +1,160 @@ +/* Declarations to interface gcj with bytecode verifier. + Copyright (C) 2003, 2004 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +Java and all Java-based marks are trademarks or registered trademarks +of Sun Microsystems, Inc. in the United States and other countries. +The Free Software Foundation is independent of Sun Microsystems, Inc. */ + +/* Written by Tom Tromey <tromey@redhat.com>. */ + +#ifndef GCC_VERIFY_H +#define GCC_VERIFY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "system.h" +#include "coretypes.h" +#include "jcf.h" +#include "tree.h" +#include "java-tree.h" + +typedef JCF vfy_constants; + +/* For our purposes a string is the same as an identifier. */ +typedef tree vfy_string; + +/* The TYPE_DECL for a class or primitive type. */ +typedef tree vfy_jclass; + +/* An unsigned jshort. */ +typedef uint16 vfy_uint_16; + +typedef struct +{ + int handler, start, end, type; +} vfy_exception; + +typedef struct +{ + tree method; + vfy_string signature; + vfy_string name; + const unsigned char *bytes; + vfy_exception *exceptions; + + /* These fields are referred to directly by the verifier. */ + vfy_jclass defining_class; + int max_stack; + int max_locals; + int code_length; + int exc_count; +} vfy_method; + +/* Entry point to the verifier. */ +int verify_jvm_instructions_new (JCF *jcf, const unsigned char *byte_ops, + long length); + +void *vfy_alloc (size_t bytes); +void vfy_free (void *mem); +bool vfy_strings_equal (vfy_string one, vfy_string two); +const char *vfy_string_bytes (vfy_string str); +int vfy_string_length (vfy_string str); +vfy_string vfy_get_string (const char *chars, int length); +vfy_string vfy_init_name (void); +vfy_string vfy_clinit_name (void); +int vfy_count_arguments (vfy_string signature); +vfy_string vfy_get_signature (vfy_method *method); +vfy_string vfy_get_method_name (vfy_method *method); +bool vfy_is_static (vfy_method *method); +const unsigned char *vfy_get_bytecode (vfy_method *method); +vfy_exception *vfy_get_exceptions (vfy_method *method); +void vfy_get_exception (vfy_exception *, int index, int *handler, + int *start, int *end, int *handler_type); +int vfy_tag (vfy_constants *pool, int index); +void vfy_load_indexes (vfy_constants *pool, int index, + vfy_uint_16 *index0, vfy_uint_16 *index1); +vfy_constants *vfy_get_constants (vfy_jclass klass); +int vfy_get_constants_size (vfy_jclass klass); +vfy_string vfy_get_pool_string (vfy_constants *pool, int index); +vfy_jclass vfy_get_pool_class (vfy_constants *pool, int index); +vfy_string vfy_make_string (const char *s, int len); +vfy_string vfy_get_class_name (vfy_jclass klass); +bool vfy_is_assignable_from (vfy_jclass target, vfy_jclass source); +char vfy_get_primitive_char (vfy_jclass klass); +int vfy_get_interface_count (vfy_jclass klass); +vfy_jclass vfy_get_interface (vfy_jclass klass, int index); +bool vfy_is_array (vfy_jclass klass); +bool vfy_is_interface (vfy_jclass klass); +bool vfy_is_primitive (vfy_jclass klass); +vfy_jclass vfy_get_superclass (vfy_jclass klass); +vfy_jclass vfy_get_array_class (vfy_jclass klass); +vfy_jclass vfy_get_component_type (vfy_jclass klass); +bool vfy_is_abstract (vfy_jclass klass); +vfy_jclass vfy_find_class (vfy_jclass klass, vfy_string name); +vfy_jclass vfy_object_type (void); +vfy_jclass vfy_string_type (void); +vfy_jclass vfy_throwable_type (void); +vfy_jclass vfy_unsuitable_type (void); +vfy_jclass vfy_return_address_type (void); +vfy_jclass vfy_null_type (void); +int vfy_fail (const char *message, int pc, vfy_jclass ignore1, + vfy_method *method); +vfy_jclass vfy_get_primitive_type (int type); +void vfy_note_stack_depth (vfy_method *method, int pc, int depth); +void vfy_note_stack_type (vfy_method *method, int pc, int slot, + vfy_jclass type); +void vfy_note_local_type (vfy_method *method, int pc, int slot, + vfy_jclass type); +void vfy_note_instruction_seen (int pc); + +#define GLOM(name, stuff) name ## stuff +#define VFY_PRIMITIVE_CLASS(name) \ + vfy_get_primitive_type ((int) (GLOM (name, _type))) + +typedef enum +{ +#define JAVAOP(name, num, ignore1, ignore2, ignore3) \ + GLOM (op_, name) = num, +#include "javaop.def" +} java_opcode; + + +#define JV_CONSTANT_Class CONSTANT_Class +#define JV_CONSTANT_ResolvedClass CONSTANT_ResolvedClass +#define JV_CONSTANT_String CONSTANT_String +#define JV_CONSTANT_ResolvedString CONSTANT_ResolvedString +#define JV_CONSTANT_Integer CONSTANT_Integer +#define JV_CONSTANT_Float CONSTANT_Float +#define JV_CONSTANT_Long CONSTANT_Long +#define JV_CONSTANT_Double CONSTANT_Double +#define JV_CONSTANT_Fieldref CONSTANT_Fieldref +#define JV_CONSTANT_InterfaceMethodref CONSTANT_InterfaceMethodref +#define JV_CONSTANT_Methodref CONSTANT_Methodref + +int verify_method (vfy_method *meth); + +#ifdef __cplusplus +} +#endif + +#endif /* ! GCC_VERIFY_H */ |