# HG changeset patch # User jrose # Date 1239213409 25200 # Node ID e5b0439ef4ae2c5fc6cb2abe7524175090dff3cb # Parent 1d037ecd79600736ddebc96c3c7de1a295bf1784 6655638: dynamic languages need method handles Summary: initial implementation, with known omissions (x86/64, sparc, compiler optim., c-oops, C++ interp.) Reviewed-by: kvn, twisti, never diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/assembler_sparc.cpp --- a/src/cpu/sparc/vm/assembler_sparc.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/sparc/vm/assembler_sparc.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -3029,6 +3029,58 @@ +void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg, + Register temp_reg, + Label& wrong_method_type) { + assert_different_registers(mtype_reg, mh_reg, temp_reg); + // compare method type against that of the receiver + RegisterOrConstant mhtype_offset = delayed_value(java_dyn_MethodHandle::type_offset_in_bytes, temp_reg); + ld_ptr(mh_reg, mhtype_offset, temp_reg); + cmp(temp_reg, mtype_reg); + br(Assembler::notEqual, false, Assembler::pn, wrong_method_type); + delayed()->nop(); +} + + +void MacroAssembler::jump_to_method_handle_entry(Register mh_reg, Register temp_reg) { + assert(mh_reg == G3_method_handle, "caller must put MH object in G3"); + assert_different_registers(mh_reg, temp_reg); + + // pick out the interpreted side of the handler + ld_ptr(mh_reg, delayed_value(java_dyn_MethodHandle::vmentry_offset_in_bytes, temp_reg), temp_reg); + + // off we go... + ld_ptr(temp_reg, MethodHandleEntry::from_interpreted_entry_offset_in_bytes(), temp_reg); + jmp(temp_reg, 0); + + // for the various stubs which take control at this point, + // see MethodHandles::generate_method_handle_stub + + // (Can any caller use this delay slot? If so, add an option for supression.) + delayed()->nop(); +} + +RegisterOrConstant MacroAssembler::argument_offset(RegisterOrConstant arg_slot, + int extra_slot_offset) { + // cf. TemplateTable::prepare_invoke(), if (load_receiver). + int stackElementSize = Interpreter::stackElementWords() * wordSize; + int offset = Interpreter::expr_offset_in_bytes(extra_slot_offset+0); + int offset1 = Interpreter::expr_offset_in_bytes(extra_slot_offset+1); + assert(offset1 - offset == stackElementSize, "correct arithmetic"); + if (arg_slot.is_constant()) { + offset += arg_slot.as_constant() * stackElementSize; + return offset; + } else { + Register temp = arg_slot.as_register(); + sll_ptr(temp, exact_log2(stackElementSize), temp); + if (offset != 0) + add(temp, offset, temp); + return temp; + } +} + + + void MacroAssembler::biased_locking_enter(Register obj_reg, Register mark_reg, Register temp_reg, Label& done, Label* slow_case, diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/assembler_sparc.hpp --- a/src/cpu/sparc/vm/assembler_sparc.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/sparc/vm/assembler_sparc.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -84,6 +84,10 @@ REGISTER_DECLARATION(Register, Gtemp , G5); +// JSR 292 fixed register usages: +REGISTER_DECLARATION(Register, G5_method_type , G5); +REGISTER_DECLARATION(Register, G3_method_handle , G3); + // The compiler requires that G5_megamorphic_method is G5_inline_cache_klass, // because a single patchable "set" instruction (NativeMovConstReg, // or NativeMovConstPatching for compiler1) instruction @@ -91,9 +95,13 @@ // call site is an inline cache or is megamorphic. See the function // CompiledIC::set_to_megamorphic. // -// On the other hand, G5_inline_cache_klass must differ from G5_method, -// because both registers are needed for an inline cache that calls -// an interpreted method. +// If a inline cache targets an interpreted method, then the +// G5 register will be used twice during the call. First, +// the call site will be patched to load a compiledICHolder +// into G5. (This is an ordered pair of ic_klass, method.) +// The c2i adapter will first check the ic_klass, then load +// G5_method with the method part of the pair just before +// jumping into the interpreter. // // Note that G5_method is only the method-self for the interpreter, // and is logically unrelated to G5_megamorphic_method. @@ -1931,6 +1939,7 @@ inline void store_ptr_contents( Register s, Address& a, int offset = 0 ); inline void jumpl_to( Address& a, Register d, int offset = 0 ); inline void jump_to( Address& a, int offset = 0 ); + inline void jump_indirect_to( Address& a, Register temp, int ld_offset = 0, int jmp_offset = 0 ); // ring buffer traceable jumps @@ -2366,6 +2375,16 @@ Register temp2_reg, Label& L_success); + // method handles (JSR 292) + void check_method_handle_type(Register mtype_reg, Register mh_reg, + Register temp_reg, + Label& wrong_method_type); + void jump_to_method_handle_entry(Register mh_reg, Register temp_reg); + // offset relative to Gargs of argument at tos[arg_slot]. + // (arg_slot == 0 means the last argument, not the first). + RegisterOrConstant argument_offset(RegisterOrConstant arg_slot, + int extra_slot_offset = 0); + // Stack overflow checking diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/assembler_sparc.inline.hpp --- a/src/cpu/sparc/vm/assembler_sparc.inline.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/sparc/vm/assembler_sparc.inline.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -671,6 +671,15 @@ } +inline void MacroAssembler::jump_indirect_to( Address& a, Register temp, + int ld_offset, int jmp_offset ) { + assert_not_delayed(); + //sethi(a); // sethi is caller responsibility for this one + ld_ptr(a, temp, ld_offset); + jmp(temp, jmp_offset); +} + + inline void MacroAssembler::set_oop( jobject obj, Register d ) { set_oop(allocate_oop_address(obj, d)); } diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/cppInterpreter_sparc.cpp --- a/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1017,6 +1017,7 @@ const int slop_factor = 2*wordSize; const int fixed_size = ((sizeof(BytecodeInterpreter) + slop_factor) >> LogBytesPerWord) + // what is the slop factor? + //6815692//methodOopDesc::extra_stack_words() + // extra push slots for MH adapters frame::memory_parameter_word_sp_offset + // register save area + param window (native ? frame::interpreter_frame_extra_outgoing_argument_words : 0); // JNI, class @@ -1163,6 +1164,9 @@ __ st_ptr(O2, XXX_STATE(_stack)); // PREPUSH __ lduh(max_stack, O3); // Full size expression stack + guarantee(!EnableMethodHandles, "no support yet for java.dyn.MethodHandle"); //6815692 + //6815692//if (EnableMethodHandles) + //6815692// __ inc(O3, methodOopDesc::extra_stack_entries()); __ sll(O3, LogBytesPerWord, O3); __ sub(O2, O3, O3); // __ sub(O3, wordSize, O3); // so prepush doesn't look out of bounds @@ -2017,7 +2021,9 @@ const int fixed_size = sizeof(BytecodeInterpreter)/wordSize + // interpreter state object frame::memory_parameter_word_sp_offset; // register save area + param window + const int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries(); return (round_to(max_stack + + extra_stack + slop_factor + fixed_size + monitor_size + @@ -2104,7 +2110,8 @@ // Need +1 here because stack_base points to the word just above the first expr stack entry // and stack_limit is supposed to point to the word just below the last expr stack entry. // See generate_compute_interpreter_state. - to_fill->_stack_limit = stack_base - (method->max_stack() + 1); + int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries(); + to_fill->_stack_limit = stack_base - (method->max_stack() + 1 + extra_stack); to_fill->_monitor_base = (BasicObjectLock*) monitor_base; // sparc specific diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/interpreterGenerator_sparc.hpp --- a/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ address generate_normal_entry(bool synchronized); address generate_native_entry(bool synchronized); address generate_abstract_entry(void); + address generate_method_handle_entry(void); address generate_math_entry(AbstractInterpreter::MethodKind kind); address generate_empty_entry(void); address generate_accessor_entry(void); diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/interpreter_sparc.cpp --- a/src/cpu/sparc/vm/interpreter_sparc.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/sparc/vm/interpreter_sparc.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -235,6 +235,19 @@ } + +// Method handle invoker +// Dispatch a method of the form java.dyn.MethodHandles::invoke(...) +address InterpreterGenerator::generate_method_handle_entry(void) { + if (!EnableMethodHandles) { + return generate_abstract_entry(); + } + return generate_abstract_entry(); //6815692// +} + + + + //---------------------------------------------------------------------------------------------------- // Entry points & stack frame layout // @@ -364,6 +377,7 @@ case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break; case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; + case Interpreter::method_handle : entry_point = ((InterpreterGenerator*)this)->generate_method_handle_entry(); break; case Interpreter::java_lang_math_sin : break; case Interpreter::java_lang_math_cos : break; case Interpreter::java_lang_math_tan : break; diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/methodHandles_sparc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/sparc/vm/methodHandles_sparc.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -0,0 +1,70 @@ +/* + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_methodHandles_sparc.cpp.incl" + +#define __ _masm-> + +address MethodHandleEntry::start_compiled_entry(MacroAssembler* _masm, + address interpreted_entry) { + __ align(wordSize); + address target = __ pc() + sizeof(Data); + while (__ pc() < target) { + __ nop(); + __ align(wordSize); + } + + MethodHandleEntry* me = (MethodHandleEntry*) __ pc(); + me->set_end_address(__ pc()); // set a temporary end_address + me->set_from_interpreted_entry(interpreted_entry); + me->set_type_checking_entry(NULL); + + return (address) me; +} + +MethodHandleEntry* MethodHandleEntry::finish_compiled_entry(MacroAssembler* _masm, + address start_addr) { + MethodHandleEntry* me = (MethodHandleEntry*) start_addr; + assert(me->end_address() == start_addr, "valid ME"); + + // Fill in the real end_address: + __ align(wordSize); + me->set_end_address(__ pc()); + + return me; +} + + +// Code generation +address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) { + ShouldNotReachHere(); //NYI, 6815692 + return NULL; +} + +// Generate an "entry" field for a method handle. +// This determines how the method handle will respond to calls. +void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHandles::EntryKind ek) { + ShouldNotReachHere(); //NYI, 6815692 +} diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/register_definitions_sparc.cpp --- a/src/cpu/sparc/vm/register_definitions_sparc.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/sparc/vm/register_definitions_sparc.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,6 +142,8 @@ REGISTER_DEFINITION(Register, G3_scratch); REGISTER_DEFINITION(Register, G4_scratch); REGISTER_DEFINITION(Register, Gtemp); +REGISTER_DEFINITION(Register, G5_method_type); +REGISTER_DEFINITION(Register, G3_method_handle); REGISTER_DEFINITION(Register, Lentry_args); #ifdef CC_INTERP diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/sharedRuntime_sparc.cpp --- a/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/sparc/vm/sharedRuntime_sparc.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -937,12 +937,12 @@ // Inputs: // G2_thread - TLS // G5_method - Method oop - // O0 - Flag telling us to restore SP from O5 - // O4_args - Pointer to interpreter's args - // O5 - Caller's saved SP, to be restored if needed + // G4 (Gargs) - Pointer to interpreter's args + // O0..O4 - free for scratch + // O5_savedSP - Caller's saved SP, to be restored if needed // O6 - Current SP! // O7 - Valid return address - // L0-L7, I0-I7 - Caller's temps (no frame pushed yet) + // L0-L7, I0-I7 - Caller's temps (no frame pushed yet) // Outputs: // G2_thread - TLS @@ -954,7 +954,7 @@ // F0-F7 - more outgoing args - // O4 is about to get loaded up with compiled callee's args + // Gargs is the incoming argument base, and also an outgoing argument. __ sub(Gargs, BytesPerWord, Gargs); #ifdef ASSERT diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/sparc/vm/templateInterpreter_sparc.cpp --- a/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -108,6 +108,24 @@ } +// Arguments are: required type in G5_method_type, and +// failing object (or NULL) in G3_method_handle. +address TemplateInterpreterGenerator::generate_WrongMethodType_handler() { + address entry = __ pc(); + // expression stack must be empty before entering the VM if an exception + // happened + __ empty_expression_stack(); + // load exception object + __ call_VM(Oexception, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_WrongMethodTypeException), + G5_method_type, // required + G3_method_handle); // actual + __ should_not_reach_here(); + return entry; +} + + address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char* name) { address entry = __ pc(); // expression stack must be empty before entering the VM if an exception happened @@ -448,6 +466,7 @@ const int extra_space = rounded_vm_local_words + // frame local scratch space + //6815692//methodOopDesc::extra_stack_words() + // extra push slots for MH adapters frame::memory_parameter_word_sp_offset + // register save area (native_call ? frame::interpreter_frame_extra_outgoing_argument_words : 0); @@ -1447,6 +1466,7 @@ round_to(callee_extra_locals * Interpreter::stackElementWords(), WordsPerLong); const int max_stack_words = max_stack * Interpreter::stackElementWords(); return (round_to((max_stack_words + //6815692//+ methodOopDesc::extra_stack_words() + rounded_vm_local_words + frame::memory_parameter_word_sp_offset), WordsPerLong) // already rounded diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/assembler_x86.cpp --- a/src/cpu/x86/vm/assembler_x86.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/assembler_x86.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -7609,6 +7609,83 @@ } +// registers on entry: +// - rax ('check' register): required MethodType +// - rcx: method handle +// - rdx, rsi, or ?: killable temp +void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg, + Register temp_reg, + Label& wrong_method_type) { + if (UseCompressedOops) unimplemented(); // field accesses must decode + // compare method type against that of the receiver + cmpptr(mtype_reg, Address(mh_reg, delayed_value(java_dyn_MethodHandle::type_offset_in_bytes, temp_reg))); + jcc(Assembler::notEqual, wrong_method_type); +} + + +// A method handle has a "vmslots" field which gives the size of its +// argument list in JVM stack slots. This field is either located directly +// in every method handle, or else is indirectly accessed through the +// method handle's MethodType. This macro hides the distinction. +void MacroAssembler::load_method_handle_vmslots(Register vmslots_reg, Register mh_reg, + Register temp_reg) { + if (UseCompressedOops) unimplemented(); // field accesses must decode + // load mh.type.form.vmslots + if (java_dyn_MethodHandle::vmslots_offset_in_bytes() != 0) { + // hoist vmslots into every mh to avoid dependent load chain + movl(vmslots_reg, Address(mh_reg, delayed_value(java_dyn_MethodHandle::vmslots_offset_in_bytes, temp_reg))); + } else { + Register temp2_reg = vmslots_reg; + movptr(temp2_reg, Address(mh_reg, delayed_value(java_dyn_MethodHandle::type_offset_in_bytes, temp_reg))); + movptr(temp2_reg, Address(temp2_reg, delayed_value(java_dyn_MethodType::form_offset_in_bytes, temp_reg))); + movl(vmslots_reg, Address(temp2_reg, delayed_value(java_dyn_MethodTypeForm::vmslots_offset_in_bytes, temp_reg))); + } +} + + +// registers on entry: +// - rcx: method handle +// - rdx: killable temp (interpreted only) +// - rax: killable temp (compiled only) +void MacroAssembler::jump_to_method_handle_entry(Register mh_reg, Register temp_reg) { + assert(mh_reg == rcx, "caller must put MH object in rcx"); + assert_different_registers(mh_reg, temp_reg); + + if (UseCompressedOops) unimplemented(); // field accesses must decode + + // pick out the interpreted side of the handler + movptr(temp_reg, Address(mh_reg, delayed_value(java_dyn_MethodHandle::vmentry_offset_in_bytes, temp_reg))); + + // off we go... + jmp(Address(temp_reg, MethodHandleEntry::from_interpreted_entry_offset_in_bytes())); + + // for the various stubs which take control at this point, + // see MethodHandles::generate_method_handle_stub +} + + +Address MacroAssembler::argument_address(RegisterOrConstant arg_slot, + int extra_slot_offset) { + // cf. TemplateTable::prepare_invoke(), if (load_receiver). + int stackElementSize = Interpreter::stackElementSize(); + int offset = Interpreter::expr_offset_in_bytes(extra_slot_offset+0); +#ifdef ASSERT + int offset1 = Interpreter::expr_offset_in_bytes(extra_slot_offset+1); + assert(offset1 - offset == stackElementSize, "correct arithmetic"); +#endif + Register scale_reg = noreg; + Address::ScaleFactor scale_factor = Address::no_scale; + if (arg_slot.is_constant()) { + offset += arg_slot.as_constant() * stackElementSize; + } else { + scale_reg = arg_slot.as_register(); + scale_factor = Address::times(stackElementSize); + } + offset += wordSize; // return PC is on stack + return Address(rsp, scale_reg, scale_factor, offset); +} + + void MacroAssembler::verify_oop_addr(Address addr, const char* s) { if (!VerifyOops) return; diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/assembler_x86.hpp --- a/src/cpu/x86/vm/assembler_x86.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/assembler_x86.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1857,6 +1857,16 @@ Register temp_reg, Label& L_success); + // method handles (JSR 292) + void check_method_handle_type(Register mtype_reg, Register mh_reg, + Register temp_reg, + Label& wrong_method_type); + void load_method_handle_vmslots(Register vmslots_reg, Register mh_reg, + Register temp_reg); + void jump_to_method_handle_entry(Register mh_reg, Register temp_reg); + Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0); + + //---- void set_word_if_not_zero(Register reg); // sets reg to 1 if not zero, otherwise 0 diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/cppInterpreter_x86.cpp --- a/src/cpu/x86/vm/cppInterpreter_x86.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/cppInterpreter_x86.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -513,10 +513,11 @@ // compute full expression stack limit const Address size_of_stack (rbx, methodOopDesc::max_stack_offset()); + const int extra_stack = 0; //6815692//methodOopDesc::extra_stack_words(); __ load_unsigned_short(rdx, size_of_stack); // get size of expression stack in words __ negptr(rdx); // so we can subtract in next step // Allocate expression stack - __ lea(rsp, Address(rsp, rdx, Address::times_ptr)); + __ lea(rsp, Address(rsp, rdx, Address::times_ptr, -extra_stack)); __ movptr(STATE(_stack_limit), rsp); } @@ -659,8 +660,9 @@ // Always give one monitor to allow us to start interp if sync method. // Any additional monitors need a check when moving the expression stack const int one_monitor = frame::interpreter_frame_monitor_size() * wordSize; + const int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries(); __ load_unsigned_short(rax, size_of_stack); // get size of expression stack in words - __ lea(rax, Address(noreg, rax, Interpreter::stackElementScale(), one_monitor)); + __ lea(rax, Address(noreg, rax, Interpreter::stackElementScale(), extra_stack + one_monitor)); __ lea(rax, Address(rax, rdx, Interpreter::stackElementScale(), overhead_size)); #ifdef ASSERT @@ -2185,6 +2187,7 @@ case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break; case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; + case Interpreter::method_handle : entry_point = ((InterpreterGenerator*)this)->generate_method_handle_entry(); break; case Interpreter::java_lang_math_sin : // fall thru case Interpreter::java_lang_math_cos : // fall thru @@ -2224,7 +2227,8 @@ const int overhead_size = sizeof(BytecodeInterpreter)/wordSize + ( frame::sender_sp_offset - frame::link_offset) + 2; - const int method_stack = (method->max_locals() + method->max_stack()) * + const int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries(); + const int method_stack = (method->max_locals() + method->max_stack() + extra_stack) * Interpreter::stackElementWords(); return overhead_size + method_stack + stub_code; } @@ -2289,7 +2293,8 @@ // Need +1 here because stack_base points to the word just above the first expr stack entry // and stack_limit is supposed to point to the word just below the last expr stack entry. // See generate_compute_interpreter_state. - to_fill->_stack_limit = stack_base - (method->max_stack() + 1); + int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries(); + to_fill->_stack_limit = stack_base - (method->max_stack() + extra_stack + 1); to_fill->_monitor_base = (BasicObjectLock*) monitor_base; to_fill->_self_link = to_fill; @@ -2335,7 +2340,8 @@ monitor_size); // Now with full size expression stack - int full_frame_size = short_frame_size + method->max_stack() * BytesPerWord; + int extra_stack = 0; //6815692//methodOopDesc::extra_stack_entries(); + int full_frame_size = short_frame_size + (method->max_stack() + extra_stack) * BytesPerWord; // and now with only live portion of the expression stack short_frame_size = short_frame_size + tempcount * BytesPerWord; diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/interp_masm_x86_32.cpp --- a/src/cpu/x86/vm/interp_masm_x86_32.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/interp_masm_x86_32.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -555,13 +555,18 @@ } -// Jump to from_interpreted entry of a call unless single stepping is possible -// in this thread in which case we must call the i2i entry -void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) { +void InterpreterMacroAssembler::prepare_to_jump_from_interpreted() { // set sender sp lea(rsi, Address(rsp, wordSize)); // record last_sp movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), rsi); +} + + +// Jump to from_interpreted entry of a call unless single stepping is possible +// in this thread in which case we must call the i2i entry +void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) { + prepare_to_jump_from_interpreted(); if (JvmtiExport::can_post_interpreter_events()) { Label run_compiled_code; diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/interp_masm_x86_32.hpp --- a/src/cpu/x86/vm/interp_masm_x86_32.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/interp_masm_x86_32.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -161,6 +161,7 @@ // jump to an invoked target + void prepare_to_jump_from_interpreted(); void jump_from_interpreted(Register method, Register temp); // Returning from interpreted functions diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/interp_masm_x86_64.cpp --- a/src/cpu/x86/vm/interp_masm_x86_64.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/interp_masm_x86_64.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -551,13 +551,18 @@ MacroAssembler::call_VM_leaf_base(entry_point, 3); } -// Jump to from_interpreted entry of a call unless single stepping is possible -// in this thread in which case we must call the i2i entry -void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) { +void InterpreterMacroAssembler::prepare_to_jump_from_interpreted() { // set sender sp lea(r13, Address(rsp, wordSize)); // record last_sp movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), r13); +} + + +// Jump to from_interpreted entry of a call unless single stepping is possible +// in this thread in which case we must call the i2i entry +void InterpreterMacroAssembler::jump_from_interpreted(Register method, Register temp) { + prepare_to_jump_from_interpreted(); if (JvmtiExport::can_post_interpreter_events()) { Label run_compiled_code; diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/interp_masm_x86_64.hpp --- a/src/cpu/x86/vm/interp_masm_x86_64.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/interp_masm_x86_64.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -176,6 +176,7 @@ void dispatch_via (TosState state, address* table); // jump to an invoked target + void prepare_to_jump_from_interpreted(); void jump_from_interpreted(Register method, Register temp); diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/interpreterGenerator_x86.hpp --- a/src/cpu/x86/vm/interpreterGenerator_x86.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/interpreterGenerator_x86.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ address generate_normal_entry(bool synchronized); address generate_native_entry(bool synchronized); address generate_abstract_entry(void); + address generate_method_handle_entry(void); address generate_math_entry(AbstractInterpreter::MethodKind kind); address generate_empty_entry(void); address generate_accessor_entry(void); diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/interpreter_x86_32.cpp --- a/src/cpu/x86/vm/interpreter_x86_32.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/interpreter_x86_32.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -201,11 +201,12 @@ address entry_point = __ pc(); // abstract method entry - // remove return address. Not really needed, since exception handling throws away expression stack - __ pop(rbx); - // adjust stack to what a normal return would do - __ mov(rsp, rsi); + // pop return address, reset last_sp to NULL + __ empty_expression_stack(); + __ restore_bcp(); // rsi must be correct for exception handler (was destroyed) + __ restore_locals(); // make sure locals pointer is correct as well (was destroyed) + // throw exception __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); // the call_VM checks for exception, so we should never return here. @@ -214,6 +215,20 @@ return entry_point; } + +// Method handle invoker +// Dispatch a method of the form java.dyn.MethodHandles::invoke(...) +address InterpreterGenerator::generate_method_handle_entry(void) { + if (!EnableMethodHandles) { + return generate_abstract_entry(); + } + + address entry_point = MethodHandles::generate_method_handle_interpreter_entry(_masm); + + return entry_point; +} + + // This method tells the deoptimizer how big an interpreted frame must be: int AbstractInterpreter::size_activation(methodOop method, int tempcount, diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/interpreter_x86_64.cpp --- a/src/cpu/x86/vm/interpreter_x86_64.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/interpreter_x86_64.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -294,6 +294,16 @@ } +// Method handle invoker +// Dispatch a method of the form java.dyn.MethodHandles::invoke(...) +address InterpreterGenerator::generate_method_handle_entry(void) { + if (!EnableMethodHandles) { + return generate_abstract_entry(); + } + return generate_abstract_entry(); //6815692// +} + + // Empty method, generate a very fast return. address InterpreterGenerator::generate_empty_entry(void) { diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/methodHandles_x86.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/x86/vm/methodHandles_x86.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -0,0 +1,1133 @@ +/* + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +#include "incls/_precompiled.incl" +#include "incls/_methodHandles_x86.cpp.incl" + +#define __ _masm-> + +address MethodHandleEntry::start_compiled_entry(MacroAssembler* _masm, + address interpreted_entry) { + // Just before the actual machine code entry point, allocate space + // for a MethodHandleEntry::Data record, so that we can manage everything + // from one base pointer. + __ align(wordSize); + address target = __ pc() + sizeof(Data); + while (__ pc() < target) { + __ nop(); + __ align(wordSize); + } + + MethodHandleEntry* me = (MethodHandleEntry*) __ pc(); + me->set_end_address(__ pc()); // set a temporary end_address + me->set_from_interpreted_entry(interpreted_entry); + me->set_type_checking_entry(NULL); + + return (address) me; +} + +MethodHandleEntry* MethodHandleEntry::finish_compiled_entry(MacroAssembler* _masm, + address start_addr) { + MethodHandleEntry* me = (MethodHandleEntry*) start_addr; + assert(me->end_address() == start_addr, "valid ME"); + + // Fill in the real end_address: + __ align(wordSize); + me->set_end_address(__ pc()); + + return me; +} + +#ifdef ASSERT +static void verify_argslot(MacroAssembler* _masm, Register rax_argslot, + const char* error_message) { + // Verify that argslot lies within (rsp, rbp]. + Label L_ok, L_bad; + __ cmpptr(rax_argslot, rbp); + __ jcc(Assembler::above, L_bad); + __ cmpptr(rsp, rax_argslot); + __ jcc(Assembler::below, L_ok); + __ bind(L_bad); + __ stop(error_message); + __ bind(L_ok); +} +#endif + + +// Code generation +address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) { + // rbx: methodOop + // rcx: receiver method handle (must load from sp[MethodTypeForm.vmslots]) + // rsi/r13: sender SP (must preserve; see prepare_to_jump_from_interpreted) + // rdx: garbage temp, blown away + + Register rbx_method = rbx; + Register rcx_recv = rcx; + Register rax_mtype = rax; + Register rdx_temp = rdx; + + // emit WrongMethodType path first, to enable jccb back-branch from main path + Label wrong_method_type; + __ bind(wrong_method_type); + __ push(rax_mtype); // required mtype + __ push(rcx_recv); // bad mh (1st stacked argument) + __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry())); + + // here's where control starts out: + __ align(CodeEntryAlignment); + address entry_point = __ pc(); + + // fetch the MethodType from the method handle into rax (the 'check' register) + { + Register tem = rbx_method; + for (jint* pchase = methodOopDesc::method_type_offsets_chain(); (*pchase) != -1; pchase++) { + __ movptr(rax_mtype, Address(tem, *pchase)); + tem = rax_mtype; // in case there is another indirection + } + } + Register rbx_temp = rbx_method; // done with incoming methodOop + + // given the MethodType, find out where the MH argument is buried + __ movptr(rdx_temp, Address(rax_mtype, + __ delayed_value(java_dyn_MethodType::form_offset_in_bytes, rbx_temp))); + __ movl(rdx_temp, Address(rdx_temp, + __ delayed_value(java_dyn_MethodTypeForm::vmslots_offset_in_bytes, rbx_temp))); + __ movptr(rcx_recv, __ argument_address(rdx_temp)); + + __ check_method_handle_type(rax_mtype, rcx_recv, rdx_temp, wrong_method_type); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + + return entry_point; +} + +// Helper to insert argument slots into the stack. +// arg_slots must be a multiple of stack_move_unit() and <= 0 +void MethodHandles::insert_arg_slots(MacroAssembler* _masm, + RegisterOrConstant arg_slots, + int arg_mask, + Register rax_argslot, + Register rbx_temp, Register rdx_temp) { + assert_different_registers(rax_argslot, rbx_temp, rdx_temp, + (!arg_slots.is_register() ? rsp : arg_slots.as_register())); + +#ifdef ASSERT + verify_argslot(_masm, rax_argslot, "insertion point must fall within current frame"); + if (arg_slots.is_register()) { + Label L_ok, L_bad; + __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD); + __ jcc(Assembler::greater, L_bad); + __ testl(arg_slots.as_register(), -stack_move_unit() - 1); + __ jcc(Assembler::zero, L_ok); + __ bind(L_bad); + __ stop("assert arg_slots <= 0 and clear low bits"); + __ bind(L_ok); + } else { + assert(arg_slots.as_constant() <= 0, ""); + assert(arg_slots.as_constant() % -stack_move_unit() == 0, ""); + } +#endif //ASSERT + +#ifdef _LP64 + if (arg_slots.is_register()) { + // clean high bits of stack motion register (was loaded as an int) + __ movslq(arg_slots.as_register(), arg_slots.as_register()); + } +#endif + + // Make space on the stack for the inserted argument(s). + // Then pull down everything shallower than rax_argslot. + // The stacked return address gets pulled down with everything else. + // That is, copy [rsp, argslot) downward by -size words. In pseudo-code: + // rsp -= size; + // for (rdx = rsp + size; rdx < argslot; rdx++) + // rdx[-size] = rdx[0] + // argslot -= size; + __ mov(rdx_temp, rsp); // source pointer for copy + __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr)); + { + Label loop; + __ bind(loop); + // pull one word down each time through the loop + __ movptr(rbx_temp, Address(rdx_temp, 0)); + __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp); + __ addptr(rdx_temp, wordSize); + __ cmpptr(rdx_temp, rax_argslot); + __ jcc(Assembler::less, loop); + } + + // Now move the argslot down, to point to the opened-up space. + __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr)); + + if (TaggedStackInterpreter && arg_mask != _INSERT_NO_MASK) { + // The caller has specified a bitmask of tags to put into the opened space. + // This only works when the arg_slots value is an assembly-time constant. + int constant_arg_slots = arg_slots.as_constant() / stack_move_unit(); + int tag_offset = Interpreter::tag_offset_in_bytes() - Interpreter::value_offset_in_bytes(); + for (int slot = 0; slot < constant_arg_slots; slot++) { + BasicType slot_type = ((arg_mask & (1 << slot)) == 0 ? T_OBJECT : T_INT); + int slot_offset = Interpreter::stackElementSize() * slot; + Address tag_addr(rax_argslot, slot_offset + tag_offset); + __ movptr(tag_addr, frame::tag_for_basic_type(slot_type)); + } + // Note that the new argument slots are tagged properly but contain + // garbage at this point. The value portions must be initialized + // by the caller. (Especially references!) + } +} + +// Helper to remove argument slots from the stack. +// arg_slots must be a multiple of stack_move_unit() and >= 0 +void MethodHandles::remove_arg_slots(MacroAssembler* _masm, + RegisterOrConstant arg_slots, + Register rax_argslot, + Register rbx_temp, Register rdx_temp) { + assert_different_registers(rax_argslot, rbx_temp, rdx_temp, + (!arg_slots.is_register() ? rsp : arg_slots.as_register())); + +#ifdef ASSERT + { + // Verify that [argslot..argslot+size) lies within (rsp, rbp). + Label L_ok, L_bad; + __ lea(rbx_temp, Address(rax_argslot, arg_slots, Address::times_ptr)); + __ cmpptr(rbx_temp, rbp); + __ jcc(Assembler::above, L_bad); + __ cmpptr(rsp, rax_argslot); + __ jcc(Assembler::below, L_ok); + __ bind(L_bad); + __ stop("deleted argument(s) must fall within current frame"); + __ bind(L_ok); + } + if (arg_slots.is_register()) { + Label L_ok, L_bad; + __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD); + __ jcc(Assembler::less, L_bad); + __ testl(arg_slots.as_register(), -stack_move_unit() - 1); + __ jcc(Assembler::zero, L_ok); + __ bind(L_bad); + __ stop("assert arg_slots >= 0 and clear low bits"); + __ bind(L_ok); + } else { + assert(arg_slots.as_constant() >= 0, ""); + assert(arg_slots.as_constant() % -stack_move_unit() == 0, ""); + } +#endif //ASSERT + +#ifdef _LP64 + if (false) { // not needed, since register is positive + // clean high bits of stack motion register (was loaded as an int) + if (arg_slots.is_register()) + __ movslq(arg_slots.as_register(), arg_slots.as_register()); + } +#endif + + // Pull up everything shallower than rax_argslot. + // Then remove the excess space on the stack. + // The stacked return address gets pulled up with everything else. + // That is, copy [rsp, argslot) upward by size words. In pseudo-code: + // for (rdx = argslot-1; rdx >= rsp; --rdx) + // rdx[size] = rdx[0] + // argslot += size; + // rsp += size; + __ lea(rdx_temp, Address(rax_argslot, -wordSize)); // source pointer for copy + { + Label loop; + __ bind(loop); + // pull one word up each time through the loop + __ movptr(rbx_temp, Address(rdx_temp, 0)); + __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp); + __ addptr(rdx_temp, -wordSize); + __ cmpptr(rdx_temp, rsp); + __ jcc(Assembler::greaterEqual, loop); + } + + // Now move the argslot up, to point to the just-copied block. + __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr)); + // And adjust the argslot address to point at the deletion point. + __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr)); +} + +#ifndef PRODUCT +void trace_method_handle_stub(const char* adaptername, + oop mh, + intptr_t* entry_sp, + intptr_t* saved_sp) { + // called as a leaf from native code: do not block the JVM! + printf("MH %s "PTR_FORMAT" "PTR_FORMAT" "INTX_FORMAT"\n", adaptername, mh, entry_sp, entry_sp - saved_sp); +} +#endif //PRODUCT + +// Generate an "entry" field for a method handle. +// This determines how the method handle will respond to calls. +void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHandles::EntryKind ek) { + // Here is the register state during an interpreted call, + // as set up by generate_method_handle_interpreter_entry(): + // - rbx: garbage temp (was MethodHandle.invoke methodOop, unused) + // - rcx: receiver method handle + // - rax: method handle type (only used by the check_mtype entry point) + // - rsi/r13: sender SP (must preserve; see prepare_to_jump_from_interpreted) + // - rdx: garbage temp, can blow away + + Register rcx_recv = rcx; + Register rax_argslot = rax; + Register rbx_temp = rbx; + Register rdx_temp = rdx; + + guarantee(java_dyn_MethodHandle::vmentry_offset_in_bytes() != 0, "must have offsets"); + + // some handy addresses + Address rbx_method_fie( rbx, methodOopDesc::from_interpreted_offset() ); + + Address rcx_mh_vmtarget( rcx_recv, java_dyn_MethodHandle::vmtarget_offset_in_bytes() ); + Address rcx_dmh_vmindex( rcx_recv, sun_dyn_DirectMethodHandle::vmindex_offset_in_bytes() ); + + Address rcx_bmh_vmargslot( rcx_recv, sun_dyn_BoundMethodHandle::vmargslot_offset_in_bytes() ); + Address rcx_bmh_argument( rcx_recv, sun_dyn_BoundMethodHandle::argument_offset_in_bytes() ); + + Address rcx_amh_vmargslot( rcx_recv, sun_dyn_AdapterMethodHandle::vmargslot_offset_in_bytes() ); + Address rcx_amh_argument( rcx_recv, sun_dyn_AdapterMethodHandle::argument_offset_in_bytes() ); + Address rcx_amh_conversion( rcx_recv, sun_dyn_AdapterMethodHandle::conversion_offset_in_bytes() ); + Address vmarg; // __ argument_address(vmargslot) + + int tag_offset = -1; + if (TaggedStackInterpreter) { + tag_offset = Interpreter::tag_offset_in_bytes() - Interpreter::value_offset_in_bytes(); + assert(tag_offset = wordSize, "stack grows as expected"); + } + + if (have_entry(ek)) { + __ nop(); // empty stubs make SG sick + return; + } + + address interp_entry = __ pc(); + if (UseCompressedOops) __ unimplemented("UseCompressedOops"); + +#ifndef PRODUCT + if (TraceMethodHandles) { + __ push(rax); __ push(rbx); __ push(rcx); __ push(rdx); __ push(rsi); __ push(rdi); + __ lea(rax, Address(rsp, wordSize*6)); // entry_sp + // arguments: + __ push(rsi); // saved_sp + __ push(rax); // entry_sp + __ push(rcx); // mh + __ push(rcx); + __ movptr(Address(rsp, 0), (intptr_t)entry_name(ek)); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), 4); + __ pop(rdi); __ pop(rsi); __ pop(rdx); __ pop(rcx); __ pop(rbx); __ pop(rax); + } +#endif //PRODUCT + + switch ((int) ek) { + case _check_mtype: + { + // this stub is special, because it requires a live mtype argument + Register rax_mtype = rax; + + // emit WrongMethodType path first, to enable jccb back-branch + Label wrong_method_type; + __ bind(wrong_method_type); + __ movptr(rdx_temp, ExternalAddress((address) &_entries[_wrong_method_type])); + __ jmp(Address(rdx_temp, MethodHandleEntry::from_interpreted_entry_offset_in_bytes())); + __ hlt(); + + interp_entry = __ pc(); + __ check_method_handle_type(rax_mtype, rcx_recv, rdx_temp, wrong_method_type); + // now rax_mtype is dead; subsequent stubs will use it as a temp + + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + } + break; + + case _wrong_method_type: + { + // this stub is special, because it requires a live mtype argument + Register rax_mtype = rax; + + interp_entry = __ pc(); + __ push(rax_mtype); // required mtype + __ push(rcx_recv); // random mh (1st stacked argument) + __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry())); + } + break; + + case _invokestatic_mh: + case _invokespecial_mh: + { + Register rbx_method = rbx_temp; + __ movptr(rbx_method, rcx_mh_vmtarget); // target is a methodOop + __ verify_oop(rbx_method); + // same as TemplateTable::invokestatic or invokespecial, + // minus the CP setup and profiling: + if (ek == _invokespecial_mh) { + // Must load & check the first argument before entering the target method. + __ load_method_handle_vmslots(rax_argslot, rcx_recv, rdx_temp); + __ movptr(rcx_recv, __ argument_address(rax_argslot, -1)); + __ null_check(rcx_recv); + __ verify_oop(rcx_recv); + } + __ jmp(rbx_method_fie); + } + break; + + case _invokevirtual_mh: + { + // same as TemplateTable::invokevirtual, + // minus the CP setup and profiling: + + // pick out the vtable index and receiver offset from the MH, + // and then we can discard it: + __ load_method_handle_vmslots(rax_argslot, rcx_recv, rdx_temp); + Register rbx_index = rbx_temp; + __ movl(rbx_index, rcx_dmh_vmindex); + // Note: The verifier allows us to ignore rcx_mh_vmtarget. + __ movptr(rcx_recv, __ argument_address(rax_argslot, -1)); + __ null_check(rcx_recv, oopDesc::klass_offset_in_bytes()); + + // get receiver klass + Register rax_klass = rax_argslot; + __ load_klass(rax_klass, rcx_recv); + __ verify_oop(rax_klass); + + // get target methodOop & entry point + const int base = instanceKlass::vtable_start_offset() * wordSize; + assert(vtableEntry::size() * wordSize == wordSize, "adjust the scaling in the code below"); + Address vtable_entry_addr(rax_klass, + rbx_index, Address::times_ptr, + base + vtableEntry::method_offset_in_bytes()); + Register rbx_method = rbx_temp; + __ movl(rbx_method, vtable_entry_addr); + + __ verify_oop(rbx_method); + __ jmp(rbx_method_fie); + } + break; + + case _invokeinterface_mh: + { + // same as TemplateTable::invokeinterface, + // minus the CP setup and profiling: + + // pick out the interface and itable index from the MH. + __ load_method_handle_vmslots(rax_argslot, rcx_recv, rdx_temp); + Register rdx_intf = rdx_temp; + Register rbx_index = rbx_temp; + __ movptr(rdx_intf, rcx_mh_vmtarget); + __ movl(rbx_index, rcx_dmh_vmindex); + __ movptr(rcx_recv, __ argument_address(rax_argslot, -1)); + __ null_check(rcx_recv, oopDesc::klass_offset_in_bytes()); + + // get receiver klass + Register rax_klass = rax_argslot; + __ load_klass(rax_klass, rcx_recv); + __ verify_oop(rax_klass); + + Register rcx_temp = rcx_recv; + Register rbx_method = rbx_index; + + // get interface klass + Label no_such_interface; + __ verify_oop(rdx_intf); + __ lookup_interface_method(rax_klass, rdx_intf, + // note: next two args must be the same: + rbx_index, rbx_method, + rcx_temp, + no_such_interface); + + __ verify_oop(rbx_method); + __ jmp(rbx_method_fie); + __ hlt(); + + __ bind(no_such_interface); + // Throw an exception. + // For historical reasons, it will be IncompatibleClassChangeError. + __ should_not_reach_here(); // %%% FIXME NYI + } + break; + + case _bound_ref_mh: + case _bound_int_mh: + case _bound_long_mh: + case _bound_ref_direct_mh: + case _bound_int_direct_mh: + case _bound_long_direct_mh: + { + bool direct_to_method = (ek >= _bound_ref_direct_mh); + BasicType arg_type = T_ILLEGAL; + if (ek == _bound_long_mh || ek == _bound_long_direct_mh) { + arg_type = T_LONG; + } else if (ek == _bound_int_mh || ek == _bound_int_direct_mh) { + arg_type = T_INT; + } else { + assert(ek == _bound_ref_mh || ek == _bound_ref_direct_mh, "must be ref"); + arg_type = T_OBJECT; + } + int arg_slots = type2size[arg_type]; + int arg_mask = (arg_type == T_OBJECT ? _INSERT_REF_MASK : + arg_slots == 1 ? _INSERT_INT_MASK : _INSERT_LONG_MASK); + + // make room for the new argument: + __ movl(rax_argslot, rcx_bmh_vmargslot); + __ lea(rax_argslot, __ argument_address(rax_argslot)); + insert_arg_slots(_masm, arg_slots * stack_move_unit(), arg_mask, + rax_argslot, rbx_temp, rdx_temp); + + // store bound argument into the new stack slot: + __ movptr(rbx_temp, rcx_bmh_argument); + Address prim_value_addr(rbx_temp, java_lang_boxing_object::value_offset_in_bytes(arg_type)); + if (arg_type == T_OBJECT) { + __ movptr(Address(rax_argslot, 0), rbx_temp); + } else { + __ load_sized_value(rbx_temp, prim_value_addr, + type2aelembytes(arg_type), is_signed_subword_type(arg_type)); + __ movptr(Address(rax_argslot, 0), rbx_temp); +#ifndef _LP64 + if (arg_slots == 2) { + __ movl(rbx_temp, prim_value_addr.plus_disp(wordSize)); + __ movl(Address(rax_argslot, Interpreter::stackElementSize()), rbx_temp); + } +#endif //_LP64 + break; + } + + if (direct_to_method) { + Register rbx_method = rbx_temp; + __ movptr(rbx_method, rcx_mh_vmtarget); + __ verify_oop(rbx_method); + __ jmp(rbx_method_fie); + } else { + __ movptr(rcx_recv, rcx_mh_vmtarget); + __ verify_oop(rcx_recv); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + } + } + break; + + case _adapter_retype_only: + // immediately jump to the next MH layer: + __ movptr(rcx_recv, rcx_mh_vmtarget); + __ verify_oop(rcx_recv); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + // This is OK when all parameter types widen. + // It is also OK when a return type narrows. + break; + + case _adapter_check_cast: + { + // temps: + Register rbx_klass = rbx_temp; // interesting AMH data + + // check a reference argument before jumping to the next layer of MH: + __ movl(rax_argslot, rcx_amh_vmargslot); + vmarg = __ argument_address(rax_argslot); + + // What class are we casting to? + __ movptr(rbx_klass, rcx_amh_argument); // this is a Class object! + __ movptr(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes())); + + // get the new MH: + __ movptr(rcx_recv, rcx_mh_vmtarget); + // (now we are done with the old MH) + + Label done; + __ movptr(rdx_temp, vmarg); + __ testl(rdx_temp, rdx_temp); + __ jcc(Assembler::zero, done); // no cast if null + __ load_klass(rdx_temp, rdx_temp); + + // live at this point: + // - rbx_klass: klass required by the target method + // - rdx_temp: argument klass to test + // - rcx_recv: method handle to invoke (after cast succeeds) + __ check_klass_subtype(rdx_temp, rbx_klass, rax_argslot, done); + + // If we get here, the type check failed! + // Call the wrong_method_type stub, passing the failing argument type in rax. + Register rax_mtype = rax_argslot; + __ push(rbx_klass); // missed klass (required type) + __ push(rdx_temp); // bad actual type (1st stacked argument) + __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry())); + + __ bind(done); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + } + break; + + case _adapter_prim_to_prim: + case _adapter_ref_to_prim: + // handled completely by optimized cases + __ stop("init_AdapterMethodHandle should not issue this"); + break; + + case _adapter_opt_i2i: // optimized subcase of adapt_prim_to_prim +//case _adapter_opt_f2i: // optimized subcase of adapt_prim_to_prim + case _adapter_opt_l2i: // optimized subcase of adapt_prim_to_prim + case _adapter_opt_unboxi: // optimized subcase of adapt_ref_to_prim + { + // perform an in-place conversion to int or an int subword + __ movl(rax_argslot, rcx_amh_vmargslot); + vmarg = __ argument_address(rax_argslot); + + switch (ek) { + case _adapter_opt_i2i: + __ movl(rdx_temp, vmarg); + break; + case _adapter_opt_l2i: + { + // just delete the extra slot; on a little-endian machine we keep the first + __ lea(rax_argslot, __ argument_address(rax_argslot, 1)); + remove_arg_slots(_masm, -stack_move_unit(), + rax_argslot, rbx_temp, rdx_temp); + vmarg = Address(rax_argslot, -Interpreter::stackElementSize()); + __ movl(rdx_temp, vmarg); + } + break; + case _adapter_opt_unboxi: + { + // Load the value up from the heap. + __ movptr(rdx_temp, vmarg); + int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_INT); +#ifdef ASSERT + for (int bt = T_BOOLEAN; bt < T_INT; bt++) { + if (is_subword_type(BasicType(bt))) + assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(BasicType(bt)), ""); + } +#endif + __ null_check(rdx_temp, value_offset); + __ movl(rdx_temp, Address(rdx_temp, value_offset)); + // We load this as a word. Because we are little-endian, + // the low bits will be correct, but the high bits may need cleaning. + // The vminfo will guide us to clean those bits. + } + break; + default: + assert(false, ""); + } + goto finish_int_conversion; + } + + finish_int_conversion: + { + Register rbx_vminfo = rbx_temp; + __ movl(rbx_vminfo, rcx_amh_conversion); + assert(CONV_VMINFO_SHIFT == 0, "preshifted"); + + // get the new MH: + __ movptr(rcx_recv, rcx_mh_vmtarget); + // (now we are done with the old MH) + + // original 32-bit vmdata word must be of this form: + // | MBZ:16 | signBitCount:8 | srcDstTypes:8 | conversionOp:8 | + __ xchgl(rcx, rbx_vminfo); // free rcx for shifts + __ shll(rdx_temp /*, rcx*/); + Label zero_extend, done; + __ testl(rcx, CONV_VMINFO_SIGN_FLAG); + __ jcc(Assembler::zero, zero_extend); + + // this path is taken for int->byte, int->short + __ sarl(rdx_temp /*, rcx*/); + __ jmp(done); + + __ bind(zero_extend); + // this is taken for int->char + __ shrl(rdx_temp /*, rcx*/); + + __ bind(done); + __ movptr(vmarg, rdx_temp); + __ xchgl(rcx, rbx_vminfo); // restore rcx_recv + + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + } + break; + + case _adapter_opt_i2l: // optimized subcase of adapt_prim_to_prim + case _adapter_opt_unboxl: // optimized subcase of adapt_ref_to_prim + { + // perform an in-place int-to-long or ref-to-long conversion + __ movl(rax_argslot, rcx_amh_vmargslot); + + // on a little-endian machine we keep the first slot and add another after + __ lea(rax_argslot, __ argument_address(rax_argslot, 1)); + insert_arg_slots(_masm, stack_move_unit(), _INSERT_INT_MASK, + rax_argslot, rbx_temp, rdx_temp); + Address vmarg1(rax_argslot, -Interpreter::stackElementSize()); + Address vmarg2 = vmarg1.plus_disp(Interpreter::stackElementSize()); + + switch (ek) { + case _adapter_opt_i2l: + { + __ movl(rdx_temp, vmarg1); + __ sarl(rdx_temp, 31); // __ extend_sign() + __ movl(vmarg2, rdx_temp); // store second word + } + break; + case _adapter_opt_unboxl: + { + // Load the value up from the heap. + __ movptr(rdx_temp, vmarg1); + int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_LONG); + assert(value_offset == java_lang_boxing_object::value_offset_in_bytes(T_DOUBLE), ""); + __ null_check(rdx_temp, value_offset); + __ movl(rbx_temp, Address(rdx_temp, value_offset + 0*BytesPerInt)); + __ movl(rdx_temp, Address(rdx_temp, value_offset + 1*BytesPerInt)); + __ movl(vmarg1, rbx_temp); + __ movl(vmarg2, rdx_temp); + } + break; + default: + assert(false, ""); + } + + __ movptr(rcx_recv, rcx_mh_vmtarget); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + } + break; + + case _adapter_opt_f2d: // optimized subcase of adapt_prim_to_prim + case _adapter_opt_d2f: // optimized subcase of adapt_prim_to_prim + { + // perform an in-place floating primitive conversion + __ movl(rax_argslot, rcx_amh_vmargslot); + __ lea(rax_argslot, __ argument_address(rax_argslot, 1)); + if (ek == _adapter_opt_f2d) { + insert_arg_slots(_masm, stack_move_unit(), _INSERT_INT_MASK, + rax_argslot, rbx_temp, rdx_temp); + } + Address vmarg(rax_argslot, -Interpreter::stackElementSize()); + +#ifdef _LP64 + if (ek == _adapter_opt_f2d) { + __ movflt(xmm0, vmarg); + __ cvtss2sd(xmm0, xmm0); + __ movdbl(vmarg, xmm0); + } else { + __ movdbl(xmm0, vmarg); + __ cvtsd2ss(xmm0, xmm0); + __ movflt(vmarg, xmm0); + } +#else //_LP64 + if (ek == _adapter_opt_f2d) { + __ fld_s(vmarg); // load float to ST0 + __ fstp_s(vmarg); // store single + } else if (!TaggedStackInterpreter) { + __ fld_d(vmarg); // load double to ST0 + __ fstp_s(vmarg); // store single + } else { + Address vmarg_tag = vmarg.plus_disp(tag_offset); + Address vmarg2 = vmarg.plus_disp(Interpreter::stackElementSize()); + // vmarg2_tag does not participate in this code + Register rbx_tag = rbx_temp; + __ movl(rbx_tag, vmarg_tag); // preserve tag + __ movl(rdx_temp, vmarg2); // get second word of double + __ movl(vmarg_tag, rdx_temp); // align with first word + __ fld_d(vmarg); // load double to ST0 + __ movl(vmarg_tag, rbx_tag); // restore tag + __ fstp_s(vmarg); // store single + } +#endif //_LP64 + + if (ek == _adapter_opt_d2f) { + remove_arg_slots(_masm, -stack_move_unit(), + rax_argslot, rbx_temp, rdx_temp); + } + + __ movptr(rcx_recv, rcx_mh_vmtarget); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + } + break; + + case _adapter_prim_to_ref: + __ unimplemented(entry_name(ek)); // %%% FIXME: NYI + break; + + case _adapter_swap_args: + case _adapter_rot_args: + // handled completely by optimized cases + __ stop("init_AdapterMethodHandle should not issue this"); + break; + + case _adapter_opt_swap_1: + case _adapter_opt_swap_2: + case _adapter_opt_rot_1_up: + case _adapter_opt_rot_1_down: + case _adapter_opt_rot_2_up: + case _adapter_opt_rot_2_down: + { + int rotate = 0, swap_slots = 0; + switch ((int)ek) { + case _adapter_opt_swap_1: swap_slots = 1; break; + case _adapter_opt_swap_2: swap_slots = 2; break; + case _adapter_opt_rot_1_up: swap_slots = 1; rotate++; break; + case _adapter_opt_rot_1_down: swap_slots = 1; rotate--; break; + case _adapter_opt_rot_2_up: swap_slots = 2; rotate++; break; + case _adapter_opt_rot_2_down: swap_slots = 2; rotate--; break; + default: assert(false, ""); + } + + // the real size of the move must be doubled if TaggedStackInterpreter: + int swap_bytes = (int)( swap_slots * Interpreter::stackElementWords() * wordSize ); + + // 'argslot' is the position of the first argument to swap + __ movl(rax_argslot, rcx_amh_vmargslot); + __ lea(rax_argslot, __ argument_address(rax_argslot)); + + // 'vminfo' is the second + Register rbx_destslot = rbx_temp; + __ movl(rbx_destslot, rcx_amh_conversion); + assert(CONV_VMINFO_SHIFT == 0, "preshifted"); + __ andl(rbx_destslot, CONV_VMINFO_MASK); + __ lea(rbx_destslot, __ argument_address(rbx_destslot)); + DEBUG_ONLY(verify_argslot(_masm, rbx_destslot, "swap point must fall within current frame")); + + if (!rotate) { + for (int i = 0; i < swap_bytes; i += wordSize) { + __ movptr(rdx_temp, Address(rax_argslot , i)); + __ push(rdx_temp); + __ movptr(rdx_temp, Address(rbx_destslot, i)); + __ movptr(Address(rax_argslot, i), rdx_temp); + __ pop(rdx_temp); + __ movptr(Address(rbx_destslot, i), rdx_temp); + } + } else { + // push the first chunk, which is going to get overwritten + for (int i = swap_bytes; (i -= wordSize) >= 0; ) { + __ movptr(rdx_temp, Address(rax_argslot, i)); + __ push(rdx_temp); + } + + if (rotate > 0) { + // rotate upward + __ subptr(rax_argslot, swap_bytes); +#ifdef ASSERT + { + // Verify that argslot > destslot, by at least swap_bytes. + Label L_ok; + __ cmpptr(rax_argslot, rbx_destslot); + __ jcc(Assembler::aboveEqual, L_ok); + __ stop("source must be above destination (upward rotation)"); + __ bind(L_ok); + } +#endif + // work argslot down to destslot, copying contiguous data upwards + // pseudo-code: + // rax = src_addr - swap_bytes + // rbx = dest_addr + // while (rax >= rbx) *(rax + swap_bytes) = *(rax + 0), rax--; + Label loop; + __ bind(loop); + __ movptr(rdx_temp, Address(rax_argslot, 0)); + __ movptr(Address(rax_argslot, swap_bytes), rdx_temp); + __ addptr(rax_argslot, -wordSize); + __ cmpptr(rax_argslot, rbx_destslot); + __ jcc(Assembler::aboveEqual, loop); + } else { + __ addptr(rax_argslot, swap_bytes); +#ifdef ASSERT + { + // Verify that argslot < destslot, by at least swap_bytes. + Label L_ok; + __ cmpptr(rax_argslot, rbx_destslot); + __ jcc(Assembler::belowEqual, L_ok); + __ stop("source must be below destination (downward rotation)"); + __ bind(L_ok); + } +#endif + // work argslot up to destslot, copying contiguous data downwards + // pseudo-code: + // rax = src_addr + swap_bytes + // rbx = dest_addr + // while (rax <= rbx) *(rax - swap_bytes) = *(rax + 0), rax++; + Label loop; + __ bind(loop); + __ movptr(rdx_temp, Address(rax_argslot, 0)); + __ movptr(Address(rax_argslot, -swap_bytes), rdx_temp); + __ addptr(rax_argslot, wordSize); + __ cmpptr(rax_argslot, rbx_destslot); + __ jcc(Assembler::belowEqual, loop); + } + + // pop the original first chunk into the destination slot, now free + for (int i = 0; i < swap_bytes; i += wordSize) { + __ pop(rdx_temp); + __ movptr(Address(rbx_destslot, i), rdx_temp); + } + } + + __ movptr(rcx_recv, rcx_mh_vmtarget); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + } + break; + + case _adapter_dup_args: + { + // 'argslot' is the position of the first argument to duplicate + __ movl(rax_argslot, rcx_amh_vmargslot); + __ lea(rax_argslot, __ argument_address(rax_argslot)); + + // 'stack_move' is negative number of words to duplicate + Register rdx_stack_move = rdx_temp; + __ movl(rdx_stack_move, rcx_amh_conversion); + __ sarl(rdx_stack_move, CONV_STACK_MOVE_SHIFT); + + int argslot0_num = 0; + Address argslot0 = __ argument_address(RegisterOrConstant(argslot0_num)); + assert(argslot0.base() == rsp, ""); + int pre_arg_size = argslot0.disp(); + assert(pre_arg_size % wordSize == 0, ""); + assert(pre_arg_size > 0, "must include PC"); + + // remember the old rsp+1 (argslot[0]) + Register rbx_oldarg = rbx_temp; + __ lea(rbx_oldarg, argslot0); + + // move rsp down to make room for dups + __ lea(rsp, Address(rsp, rdx_stack_move, Address::times_ptr)); + + // compute the new rsp+1 (argslot[0]) + Register rdx_newarg = rdx_temp; + __ lea(rdx_newarg, argslot0); + + __ push(rdi); // need a temp + // (preceding push must be done after arg addresses are taken!) + + // pull down the pre_arg_size data (PC) + for (int i = -pre_arg_size; i < 0; i += wordSize) { + __ movptr(rdi, Address(rbx_oldarg, i)); + __ movptr(Address(rdx_newarg, i), rdi); + } + + // copy from rax_argslot[0...] down to new_rsp[1...] + // pseudo-code: + // rbx = old_rsp+1 + // rdx = new_rsp+1 + // rax = argslot + // while (rdx < rbx) *rdx++ = *rax++ + Label loop; + __ bind(loop); + __ movptr(rdi, Address(rax_argslot, 0)); + __ movptr(Address(rdx_newarg, 0), rdi); + __ addptr(rax_argslot, wordSize); + __ addptr(rdx_newarg, wordSize); + __ cmpptr(rdx_newarg, rbx_oldarg); + __ jcc(Assembler::less, loop); + + __ pop(rdi); // restore temp + + __ movptr(rcx_recv, rcx_mh_vmtarget); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + } + break; + + case _adapter_drop_args: + { + // 'argslot' is the position of the first argument to nuke + __ movl(rax_argslot, rcx_amh_vmargslot); + __ lea(rax_argslot, __ argument_address(rax_argslot)); + + __ push(rdi); // need a temp + // (must do previous push after argslot address is taken) + + // 'stack_move' is number of words to drop + Register rdi_stack_move = rdi; + __ movl(rdi_stack_move, rcx_amh_conversion); + __ sarl(rdi_stack_move, CONV_STACK_MOVE_SHIFT); + remove_arg_slots(_masm, rdi_stack_move, + rax_argslot, rbx_temp, rdx_temp); + + __ pop(rdi); // restore temp + + __ movptr(rcx_recv, rcx_mh_vmtarget); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + } + break; + + case _adapter_collect_args: + __ unimplemented(entry_name(ek)); // %%% FIXME: NYI + break; + + case _adapter_spread_args: + // handled completely by optimized cases + __ stop("init_AdapterMethodHandle should not issue this"); + break; + + case _adapter_opt_spread_0: + case _adapter_opt_spread_1: + case _adapter_opt_spread_more: + { + // spread an array out into a group of arguments + int length_constant = -1; + switch (ek) { + case _adapter_opt_spread_0: length_constant = 0; break; + case _adapter_opt_spread_1: length_constant = 1; break; + } + + // find the address of the array argument + __ movl(rax_argslot, rcx_amh_vmargslot); + __ lea(rax_argslot, __ argument_address(rax_argslot)); + + // grab some temps + { __ push(rsi); __ push(rdi); } + // (preceding pushes must be done after argslot address is taken!) +#define UNPUSH_RSI_RDI \ + { __ pop(rdi); __ pop(rsi); } + + // arx_argslot points both to the array and to the first output arg + vmarg = Address(rax_argslot, 0); + + // Get the array value. + Register rsi_array = rsi; + Register rdx_array_klass = rdx_temp; + BasicType elem_type = T_OBJECT; + int length_offset = arrayOopDesc::length_offset_in_bytes(); + int elem0_offset = arrayOopDesc::base_offset_in_bytes(elem_type); + __ movptr(rsi_array, vmarg); + Label skip_array_check; + if (length_constant == 0) { + __ testptr(rsi_array, rsi_array); + __ jcc(Assembler::zero, skip_array_check); + } + __ null_check(rsi_array, oopDesc::klass_offset_in_bytes()); + __ load_klass(rdx_array_klass, rsi_array); + + // Check the array type. + Register rbx_klass = rbx_temp; + __ movptr(rbx_klass, rcx_amh_argument); // this is a Class object! + __ movptr(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes())); + + Label ok_array_klass, bad_array_klass, bad_array_length; + __ check_klass_subtype(rdx_array_klass, rbx_klass, rdi, ok_array_klass); + // If we get here, the type check failed! + __ jmp(bad_array_klass); + __ bind(ok_array_klass); + + // Check length. + if (length_constant >= 0) { + __ cmpl(Address(rsi_array, length_offset), length_constant); + } else { + Register rbx_vminfo = rbx_temp; + __ movl(rbx_vminfo, rcx_amh_conversion); + assert(CONV_VMINFO_SHIFT == 0, "preshifted"); + __ andl(rbx_vminfo, CONV_VMINFO_MASK); + __ cmpl(rbx_vminfo, Address(rsi_array, length_offset)); + } + __ jcc(Assembler::notEqual, bad_array_length); + + Register rdx_argslot_limit = rdx_temp; + + // Array length checks out. Now insert any required stack slots. + if (length_constant == -1) { + // Form a pointer to the end of the affected region. + __ lea(rdx_argslot_limit, Address(rax_argslot, Interpreter::stackElementSize())); + // 'stack_move' is negative number of words to insert + Register rdi_stack_move = rdi; + __ movl(rdi_stack_move, rcx_amh_conversion); + __ sarl(rdi_stack_move, CONV_STACK_MOVE_SHIFT); + Register rsi_temp = rsi_array; // spill this + insert_arg_slots(_masm, rdi_stack_move, -1, + rax_argslot, rbx_temp, rsi_temp); + // reload the array (since rsi was killed) + __ movptr(rsi_array, vmarg); + } else if (length_constant > 1) { + int arg_mask = 0; + int new_slots = (length_constant - 1); + for (int i = 0; i < new_slots; i++) { + arg_mask <<= 1; + arg_mask |= _INSERT_REF_MASK; + } + insert_arg_slots(_masm, new_slots * stack_move_unit(), arg_mask, + rax_argslot, rbx_temp, rdx_temp); + } else if (length_constant == 1) { + // no stack resizing required + } else if (length_constant == 0) { + remove_arg_slots(_masm, -stack_move_unit(), + rax_argslot, rbx_temp, rdx_temp); + } + + // Copy from the array to the new slots. + // Note: Stack change code preserves integrity of rax_argslot pointer. + // So even after slot insertions, rax_argslot still points to first argument. + if (length_constant == -1) { + // [rax_argslot, rdx_argslot_limit) is the area we are inserting into. + Register rsi_source = rsi_array; + __ lea(rsi_source, Address(rsi_array, elem0_offset)); + Label loop; + __ bind(loop); + __ movptr(rbx_temp, Address(rsi_source, 0)); + __ movptr(Address(rax_argslot, 0), rbx_temp); + __ addptr(rsi_source, type2aelembytes(elem_type)); + if (TaggedStackInterpreter) { + __ movptr(Address(rax_argslot, tag_offset), + frame::tag_for_basic_type(elem_type)); + } + __ addptr(rax_argslot, Interpreter::stackElementSize()); + __ cmpptr(rax_argslot, rdx_argslot_limit); + __ jcc(Assembler::less, loop); + } else if (length_constant == 0) { + __ bind(skip_array_check); + // nothing to copy + } else { + int elem_offset = elem0_offset; + int slot_offset = 0; + for (int index = 0; index < length_constant; index++) { + __ movptr(rbx_temp, Address(rsi_array, elem_offset)); + __ movptr(Address(rax_argslot, slot_offset), rbx_temp); + elem_offset += type2aelembytes(elem_type); + if (TaggedStackInterpreter) { + __ movptr(Address(rax_argslot, slot_offset + tag_offset), + frame::tag_for_basic_type(elem_type)); + } + slot_offset += Interpreter::stackElementSize(); + } + } + + // Arguments are spread. Move to next method handle. + UNPUSH_RSI_RDI; + __ movptr(rcx_recv, rcx_mh_vmtarget); + __ jump_to_method_handle_entry(rcx_recv, rdx_temp); + + __ bind(bad_array_klass); + UNPUSH_RSI_RDI; + __ stop("bad array klass NYI"); + + __ bind(bad_array_length); + UNPUSH_RSI_RDI; + __ stop("bad array length NYI"); + +#undef UNPUSH_RSI_RDI + } + break; + + case _adapter_flyby: + case _adapter_ricochet: + __ unimplemented(entry_name(ek)); // %%% FIXME: NYI + break; + + default: ShouldNotReachHere(); + } + __ hlt(); + + address me_cookie = MethodHandleEntry::start_compiled_entry(_masm, interp_entry); + __ unimplemented(entry_name(ek)); // %%% FIXME: NYI + + init_entry(ek, MethodHandleEntry::finish_compiled_entry(_masm, me_cookie)); +} diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/stubGenerator_x86_32.cpp --- a/src/cpu/x86/vm/stubGenerator_x86_32.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/stubGenerator_x86_32.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -2219,6 +2219,16 @@ // arraycopy stubs used by compilers generate_arraycopy_stubs(); + + // generic method handle stubs + if (EnableMethodHandles && SystemDictionary::MethodHandle_klass() != NULL) { + for (MethodHandles::EntryKind ek = MethodHandles::_EK_FIRST; + ek < MethodHandles::_EK_LIMIT; + ek = MethodHandles::EntryKind(1 + (int)ek)) { + StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek)); + MethodHandles::generate_method_handle_stub(_masm, ek); + } + } } diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/templateInterpreter_x86_32.cpp --- a/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -92,6 +92,33 @@ return entry; } +// Arguments are: required type at TOS+8, failing object (or NULL) at TOS+4. +// pc at TOS (just for debugging) +address TemplateInterpreterGenerator::generate_WrongMethodType_handler() { + address entry = __ pc(); + + __ pop(rbx); // actual failing object is at TOS + __ pop(rax); // required type is at TOS+4 + + __ verify_oop(rbx); + __ verify_oop(rax); + + // Various method handle types use interpreter registers as temps. + __ restore_bcp(); + __ restore_locals(); + + // Expression stack must be empty before entering the VM for an exception. + __ empty_expression_stack(); + __ empty_FPU_stack(); + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_WrongMethodTypeException), + // pass required type, failing object (or NULL) + rax, rbx); + return entry; +} + + address TemplateInterpreterGenerator::generate_exception_handler_common(const char* name, const char* message, bool pass_oop) { assert(!pass_oop || message == NULL, "either oop or message but not both"); address entry = __ pc(); @@ -1370,6 +1397,7 @@ case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break; case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; + case Interpreter::method_handle : entry_point = ((InterpreterGenerator*)this)->generate_method_handle_entry(); break; case Interpreter::java_lang_math_sin : // fall thru case Interpreter::java_lang_math_cos : // fall thru @@ -1400,7 +1428,8 @@ // be sure to change this if you add/subtract anything to/from the overhead area const int overhead_size = -frame::interpreter_frame_initial_sp_offset; - const int method_stack = (method->max_locals() + method->max_stack()) * + const int extra_stack = methodOopDesc::extra_stack_entries(); + const int method_stack = (method->max_locals() + method->max_stack() + extra_stack) * Interpreter::stackElementWords(); return overhead_size + method_stack + stub_code; } diff -r 1d037ecd7960 -r e5b0439ef4ae src/cpu/x86/vm/templateInterpreter_x86_64.cpp --- a/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,6 +100,26 @@ return entry; } +// Arguments are: required type in rarg1, failing object (or NULL) in rarg2 +address TemplateInterpreterGenerator::generate_WrongMethodType_handler() { + address entry = __ pc(); + + __ pop(c_rarg2); // failing object is at TOS + __ pop(c_rarg1); // required type is at TOS+8 + + // expression stack must be empty before entering the VM if an + // exception happened + __ empty_expression_stack(); + + __ call_VM(noreg, + CAST_FROM_FN_PTR(address, + InterpreterRuntime:: + throw_WrongMethodTypeException), + // pass required type, failing object (or NULL) + c_rarg1, c_rarg2); + return entry; +} + address TemplateInterpreterGenerator::generate_exception_handler_common( const char* name, const char* message, bool pass_oop) { assert(!pass_oop || message == NULL, "either oop or message but not both"); @@ -1393,6 +1413,7 @@ case Interpreter::empty : entry_point = ((InterpreterGenerator*) this)->generate_empty_entry(); break; case Interpreter::accessor : entry_point = ((InterpreterGenerator*) this)->generate_accessor_entry(); break; case Interpreter::abstract : entry_point = ((InterpreterGenerator*) this)->generate_abstract_entry(); break; + case Interpreter::method_handle : entry_point = ((InterpreterGenerator*) this)->generate_method_handle_entry();break; case Interpreter::java_lang_math_sin : // fall thru case Interpreter::java_lang_math_cos : // fall thru @@ -1423,7 +1444,8 @@ -(frame::interpreter_frame_initial_sp_offset) + entry_size; const int stub_code = frame::entry_frame_after_call_words; - const int method_stack = (method->max_locals() + method->max_stack()) * + const int extra_stack = methodOopDesc::extra_stack_entries(); + const int method_stack = (method->max_locals() + method->max_stack() + extra_stack) * Interpreter::stackElementWords(); return (overhead_size + method_stack + stub_code); } diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/ci/ciMethod.cpp --- a/src/share/vm/ci/ciMethod.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/ci/ciMethod.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -675,6 +675,30 @@ } // ------------------------------------------------------------------ +// invokedynamic support +// +bool ciMethod::is_method_handle_invoke() { + check_is_loaded(); + bool flag = ((flags().as_int() & JVM_MH_INVOKE_BITS) == JVM_MH_INVOKE_BITS); +#ifdef ASSERT + { + VM_ENTRY_MARK; + bool flag2 = get_methodOop()->is_method_handle_invoke(); + assert(flag == flag2, "consistent"); + } +#endif //ASSERT + return flag; +} + +ciInstance* ciMethod::method_handle_type() { + check_is_loaded(); + VM_ENTRY_MARK; + oop mtype = get_methodOop()->method_handle_type(); + return CURRENT_THREAD_ENV->get_object(mtype)->as_instance(); +} + + +// ------------------------------------------------------------------ // ciMethod::build_method_data // // Generate new methodDataOop objects at compile time. diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/ci/ciMethod.hpp --- a/src/share/vm/ci/ciMethod.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/ci/ciMethod.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -207,6 +207,8 @@ bool check_call(int refinfo_index, bool is_static) const; void build_method_data(); // make sure it exists in the VM also int scale_count(int count, float prof_factor = 1.); // make MDO count commensurate with IIC + bool is_method_handle_invoke(); + ciInstance* method_handle_type(); // What kind of ciObject is this? bool is_method() { return true; } diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/classfile/classFileParser.cpp --- a/src/share/vm/classfile/classFileParser.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/classfile/classFileParser.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1842,6 +1842,11 @@ _has_vanilla_constructor = true; } + if (EnableMethodHandles && m->is_method_handle_invoke()) { + THROW_MSG_(vmSymbols::java_lang_VirtualMachineError(), + "Method handle invokers must be defined internally to the VM", nullHandle); + } + return m; } @@ -2465,9 +2470,84 @@ } +// Force MethodHandle.vmentry to be an unmanaged pointer. +// There is no way for a classfile to express this, so we must help it. +void ClassFileParser::java_dyn_MethodHandle_fix_pre(constantPoolHandle cp, + typeArrayHandle* fields_ptr, + FieldAllocationCount *fac_ptr, + TRAPS) { + // Add fake fields for java.dyn.MethodHandle instances + // + // This is not particularly nice, but since there is no way to express + // a native wordSize field in Java, we must do it at this level. + + if (!EnableMethodHandles) return; + + int word_sig_index = 0; + const int cp_size = cp->length(); + for (int index = 1; index < cp_size; index++) { + if (cp->tag_at(index).is_utf8() && + cp->symbol_at(index) == vmSymbols::machine_word_signature()) { + word_sig_index = index; + break; + } + } + + if (word_sig_index == 0) + THROW_MSG(vmSymbols::java_lang_VirtualMachineError(), + "missing I or J signature (for vmentry) in java.dyn.MethodHandle"); + + bool found_vmentry = false; + + const int n = (*fields_ptr)()->length(); + for (int i = 0; i < n; i += instanceKlass::next_offset) { + int name_index = (*fields_ptr)->ushort_at(i + instanceKlass::name_index_offset); + int sig_index = (*fields_ptr)->ushort_at(i + instanceKlass::signature_index_offset); + int acc_flags = (*fields_ptr)->ushort_at(i + instanceKlass::access_flags_offset); + symbolOop f_name = cp->symbol_at(name_index); + symbolOop f_sig = cp->symbol_at(sig_index); + if (f_sig == vmSymbols::byte_signature() && + f_name == vmSymbols::vmentry_name() && + (acc_flags & JVM_ACC_STATIC) == 0) { + // Adjust the field type from byte to an unmanaged pointer. + assert(fac_ptr->nonstatic_byte_count > 0, ""); + fac_ptr->nonstatic_byte_count -= 1; + (*fields_ptr)->ushort_at_put(i + instanceKlass::signature_index_offset, + word_sig_index); + if (wordSize == jintSize) { + fac_ptr->nonstatic_word_count += 1; + } else { + fac_ptr->nonstatic_double_count += 1; + } + + FieldAllocationType atype = (FieldAllocationType) (*fields_ptr)->ushort_at(i+4); + assert(atype == NONSTATIC_BYTE, ""); + FieldAllocationType new_atype = NONSTATIC_WORD; + if (wordSize > jintSize) { + if (Universe::field_type_should_be_aligned(T_LONG)) { + atype = NONSTATIC_ALIGNED_DOUBLE; + } else { + atype = NONSTATIC_DOUBLE; + } + } + (*fields_ptr)->ushort_at_put(i+4, new_atype); + + found_vmentry = true; + break; + } + } + + if (!found_vmentry) + THROW_MSG(vmSymbols::java_lang_VirtualMachineError(), + "missing vmentry byte field in java.dyn.MethodHandle"); + +} + + instanceKlassHandle ClassFileParser::parseClassFile(symbolHandle name, Handle class_loader, Handle protection_domain, + KlassHandle host_klass, GrowableArray* cp_patches, symbolHandle& parsed_name, TRAPS) { @@ -2500,6 +2580,7 @@ } } + _host_klass = host_klass; _cp_patches = cp_patches; instanceKlassHandle nullHandle; @@ -2808,6 +2889,11 @@ java_lang_Class_fix_pre(&methods, &fac, CHECK_(nullHandle)); } + // adjust the vmentry field declaration in java.dyn.MethodHandle + if (EnableMethodHandles && class_name() == vmSymbols::sun_dyn_MethodHandleImpl() && class_loader.is_null()) { + java_dyn_MethodHandle_fix_pre(cp, &fields, &fac, CHECK_(nullHandle)); + } + // Add a fake "discovered" field if it is not present // for compatibility with earlier jdk's. if (class_name() == vmSymbols::java_lang_ref_Reference() @@ -3134,7 +3220,7 @@ this_klass->set_method_ordering(method_ordering()); this_klass->set_initial_method_idnum(methods->length()); this_klass->set_name(cp->klass_name_at(this_class_index)); - if (LinkWellKnownClasses) // I am well known to myself + if (LinkWellKnownClasses || is_anonymous()) // I am well known to myself cp->klass_at_put(this_class_index, this_klass()); // eagerly resolve this_klass->set_protection_domain(protection_domain()); this_klass->set_fields_annotations(fields_annotations()); diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/classfile/classFileParser.hpp --- a/src/share/vm/classfile/classFileParser.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/classfile/classFileParser.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ u2 _major_version; u2 _minor_version; symbolHandle _class_name; + KlassHandle _host_klass; GrowableArray* _cp_patches; // overrides for CP entries bool _has_finalizer; @@ -145,6 +146,11 @@ // Adjust the next_nonstatic_oop_offset to place the fake fields // before any Java fields. void java_lang_Class_fix_post(int* next_nonstatic_oop_offset); + // Adjust the field allocation counts for java.dyn.MethodHandle to add + // a fake address (void*) field. + void java_dyn_MethodHandle_fix_pre(constantPoolHandle cp, + typeArrayHandle* fields_ptr, + FieldAllocationCount *fac_ptr, TRAPS); // Format checker methods void classfile_parse_error(const char* msg, TRAPS); @@ -204,6 +210,10 @@ char* skip_over_field_name(char* name, bool slash_ok, unsigned int length); char* skip_over_field_signature(char* signature, bool void_ok, unsigned int length, TRAPS); + bool is_anonymous() { + assert(AnonymousClasses || _host_klass.is_null(), ""); + return _host_klass.not_null(); + } bool has_cp_patch_at(int index) { assert(AnonymousClasses, ""); assert(index >= 0, "oob"); @@ -249,11 +259,13 @@ Handle protection_domain, symbolHandle& parsed_name, TRAPS) { - return parseClassFile(name, class_loader, protection_domain, NULL, parsed_name, THREAD); + KlassHandle no_host_klass; + return parseClassFile(name, class_loader, protection_domain, no_host_klass, NULL, parsed_name, THREAD); } instanceKlassHandle parseClassFile(symbolHandle name, Handle class_loader, Handle protection_domain, + KlassHandle host_klass, GrowableArray* cp_patches, symbolHandle& parsed_name, TRAPS); diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/classfile/dictionary.cpp --- a/src/share/vm/classfile/dictionary.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/classfile/dictionary.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -549,6 +549,63 @@ } } +SymbolPropertyTable::SymbolPropertyTable(int table_size) + : Hashtable(table_size, sizeof(SymbolPropertyEntry)) +{ +} +SymbolPropertyTable::SymbolPropertyTable(int table_size, HashtableBucket* t, + int number_of_entries) + : Hashtable(table_size, sizeof(SymbolPropertyEntry), t, number_of_entries) +{ +} + + +SymbolPropertyEntry* SymbolPropertyTable::find_entry(int index, unsigned int hash, + symbolHandle sym) { + assert(index == index_for(sym), "incorrect index?"); + for (SymbolPropertyEntry* p = bucket(index); p != NULL; p = p->next()) { + if (p->hash() == hash && p->symbol() == sym()) { + return p; + } + } + return NULL; +} + + +SymbolPropertyEntry* SymbolPropertyTable::add_entry(int index, unsigned int hash, + symbolHandle sym) { + assert_locked_or_safepoint(SystemDictionary_lock); + assert(index == index_for(sym), "incorrect index?"); + assert(find_entry(index, hash, sym) == NULL, "no double entry"); + + SymbolPropertyEntry* p = new_entry(hash, sym()); + Hashtable::add_entry(index, p); + return p; +} + + +void SymbolPropertyTable::oops_do(OopClosure* f) { + for (int index = 0; index < table_size(); index++) { + for (SymbolPropertyEntry* p = bucket(index); p != NULL; p = p->next()) { + f->do_oop((oop*) p->symbol_addr()); + if (p->property_oop() != NULL) { + f->do_oop(p->property_oop_addr()); + } + } + } +} + +void SymbolPropertyTable::methods_do(void f(methodOop)) { + for (int index = 0; index < table_size(); index++) { + for (SymbolPropertyEntry* p = bucket(index); p != NULL; p = p->next()) { + oop prop = p->property_oop(); + if (prop != NULL && prop->is_method()) { + f((methodOop)prop); + } + } + } +} + // ---------------------------------------------------------------------------- #ifndef PRODUCT diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/classfile/dictionary.hpp --- a/src/share/vm/classfile/dictionary.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/classfile/dictionary.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -217,3 +217,112 @@ tty->print_cr("pd set = #%d", count); } }; + +// Entry in a SymbolPropertyTable, mapping a single symbolOop +// to a managed and an unmanaged pointer. +class SymbolPropertyEntry : public HashtableEntry { + friend class VMStructs; + private: + oop _property_oop; + address _property_data; + + public: + symbolOop symbol() const { return (symbolOop) literal(); } + + oop property_oop() const { return _property_oop; } + void set_property_oop(oop p) { _property_oop = p; } + + address property_data() const { return _property_data; } + void set_property_data(address p) { _property_data = p; } + + SymbolPropertyEntry* next() const { + return (SymbolPropertyEntry*)HashtableEntry::next(); + } + + SymbolPropertyEntry** next_addr() { + return (SymbolPropertyEntry**)HashtableEntry::next_addr(); + } + + oop* symbol_addr() { return literal_addr(); } + oop* property_oop_addr() { return &_property_oop; } + + void print_on(outputStream* st) const { + symbol()->print_value_on(st); + st->print(" -> "); + bool printed = false; + if (property_oop() != NULL) { + property_oop()->print_value_on(st); + printed = true; + } + if (property_data() != NULL) { + if (printed) st->print(" and "); + st->print(INTPTR_FORMAT, property_data()); + printed = true; + } + st->print_cr(printed ? "" : "(empty)"); + } +}; + +// A system-internal mapping of symbols to pointers, both managed +// and unmanaged. Used to record the auto-generation of each method +// MethodHandle.invoke(S)T, for all signatures (S)T. +class SymbolPropertyTable : public Hashtable { + friend class VMStructs; +private: + SymbolPropertyEntry* bucket(int i) { + return (SymbolPropertyEntry*) Hashtable::bucket(i); + } + + // The following method is not MT-safe and must be done under lock. + SymbolPropertyEntry** bucket_addr(int i) { + return (SymbolPropertyEntry**) Hashtable::bucket_addr(i); + } + + void add_entry(int index, SymbolPropertyEntry* new_entry) { + ShouldNotReachHere(); + } + void set_entry(int index, SymbolPropertyEntry* new_entry) { + ShouldNotReachHere(); + } + + SymbolPropertyEntry* new_entry(unsigned int hash, symbolOop symbol) { + SymbolPropertyEntry* entry = (SymbolPropertyEntry*) Hashtable::new_entry(hash, symbol); + entry->set_property_oop(NULL); + entry->set_property_data(NULL); + return entry; + } + +public: + SymbolPropertyTable(int table_size); + SymbolPropertyTable(int table_size, HashtableBucket* t, int number_of_entries); + + void free_entry(SymbolPropertyEntry* entry) { + Hashtable::free_entry(entry); + } + + unsigned int compute_hash(symbolHandle sym) { + // Use the regular identity_hash. + return Hashtable::compute_hash(sym); + } + + // need not be locked; no state change + SymbolPropertyEntry* find_entry(int index, unsigned int hash, symbolHandle name); + + // must be done under SystemDictionary_lock + SymbolPropertyEntry* add_entry(int index, unsigned int hash, symbolHandle name); + + // GC support + void oops_do(OopClosure* f); + void methods_do(void f(methodOop)); + + // Sharing support + void dump(SerializeOopClosure* soc); + void restore(SerializeOopClosure* soc); + void reorder_dictionary(); + +#ifndef PRODUCT + void print(); +#endif + void verify(); +}; + diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/classfile/javaClasses.cpp --- a/src/share/vm/classfile/javaClasses.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/classfile/javaClasses.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -25,13 +25,24 @@ # include "incls/_precompiled.incl" # include "incls/_javaClasses.cpp.incl" +static bool find_field(instanceKlass* ik, + symbolOop name_symbol, symbolOop signature_symbol, + fieldDescriptor* fd, + bool allow_super = false) { + if (allow_super) + return ik->find_field(name_symbol, signature_symbol, fd) != NULL; + else + return ik->find_local_field(name_symbol, signature_symbol, fd); +} + // Helpful routine for computing field offsets at run time rather than hardcoding them static void compute_offset(int &dest_offset, - klassOop klass_oop, symbolOop name_symbol, symbolOop signature_symbol) { + klassOop klass_oop, symbolOop name_symbol, symbolOop signature_symbol, + bool allow_super = false) { fieldDescriptor fd; instanceKlass* ik = instanceKlass::cast(klass_oop); - if (!ik->find_local_field(name_symbol, signature_symbol, &fd)) { + if (!find_field(ik, name_symbol, signature_symbol, &fd, allow_super)) { ResourceMark rm; tty->print_cr("Invalid layout of %s at %s", ik->external_name(), name_symbol->as_C_string()); fatal("Invalid layout of preloaded class"); @@ -42,14 +53,16 @@ // Same as above but for "optional" offsets that might not be present in certain JDK versions static void compute_optional_offset(int& dest_offset, - klassOop klass_oop, symbolOop name_symbol, symbolOop signature_symbol) { + klassOop klass_oop, symbolOop name_symbol, symbolOop signature_symbol, + bool allow_super = false) { fieldDescriptor fd; instanceKlass* ik = instanceKlass::cast(klass_oop); - if (ik->find_local_field(name_symbol, signature_symbol, &fd)) { + if (find_field(ik, name_symbol, signature_symbol, &fd, allow_super)) { dest_offset = fd.offset(); } } + Handle java_lang_String::basic_create(int length, bool tenured, TRAPS) { // Create the String object first, so there's a chance that the String // and the char array it points to end up in the same cache line. @@ -2107,13 +2120,324 @@ } +// Support for java_dyn_MethodHandle + +int java_dyn_MethodHandle::_type_offset; +int java_dyn_MethodHandle::_vmtarget_offset; +int java_dyn_MethodHandle::_vmentry_offset; +int java_dyn_MethodHandle::_vmslots_offset; + +int sun_dyn_MemberName::_clazz_offset; +int sun_dyn_MemberName::_name_offset; +int sun_dyn_MemberName::_type_offset; +int sun_dyn_MemberName::_flags_offset; +int sun_dyn_MemberName::_vmtarget_offset; +int sun_dyn_MemberName::_vmindex_offset; + +int sun_dyn_DirectMethodHandle::_vmindex_offset; + +int sun_dyn_BoundMethodHandle::_argument_offset; +int sun_dyn_BoundMethodHandle::_vmargslot_offset; + +int sun_dyn_AdapterMethodHandle::_conversion_offset; + +void java_dyn_MethodHandle::compute_offsets() { + klassOop k = SystemDictionary::MethodHandle_klass(); + if (k != NULL && EnableMethodHandles) { + compute_offset(_type_offset, k, vmSymbols::type_name(), vmSymbols::java_dyn_MethodType_signature(), true); + compute_offset(_vmtarget_offset, k, vmSymbols::vmtarget_name(), vmSymbols::object_signature(), true); + compute_offset(_vmentry_offset, k, vmSymbols::vmentry_name(), vmSymbols::machine_word_signature(), true); + + // Note: MH.vmslots (if it is present) is a hoisted copy of MH.type.form.vmslots. + // It is optional pending experiments to keep or toss. + compute_optional_offset(_vmslots_offset, k, vmSymbols::vmslots_name(), vmSymbols::int_signature(), true); + } +} + +void sun_dyn_MemberName::compute_offsets() { + klassOop k = SystemDictionary::MemberName_klass(); + if (k != NULL && EnableMethodHandles) { + compute_offset(_clazz_offset, k, vmSymbols::clazz_name(), vmSymbols::class_signature()); + compute_offset(_name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature()); + compute_offset(_type_offset, k, vmSymbols::type_name(), vmSymbols::object_signature()); + compute_offset(_flags_offset, k, vmSymbols::flags_name(), vmSymbols::int_signature()); + compute_offset(_vmtarget_offset, k, vmSymbols::vmtarget_name(), vmSymbols::object_signature()); + compute_offset(_vmindex_offset, k, vmSymbols::vmindex_name(), vmSymbols::int_signature()); + } +} + +void sun_dyn_DirectMethodHandle::compute_offsets() { + klassOop k = SystemDictionary::DirectMethodHandle_klass(); + if (k != NULL && EnableMethodHandles) { + compute_offset(_vmindex_offset, k, vmSymbols::vmindex_name(), vmSymbols::int_signature(), true); + } +} + +void sun_dyn_BoundMethodHandle::compute_offsets() { + klassOop k = SystemDictionary::BoundMethodHandle_klass(); + if (k != NULL && EnableMethodHandles) { + compute_offset(_vmargslot_offset, k, vmSymbols::vmargslot_name(), vmSymbols::int_signature(), true); + compute_offset(_argument_offset, k, vmSymbols::argument_name(), vmSymbols::object_signature(), true); + } +} + +void sun_dyn_AdapterMethodHandle::compute_offsets() { + klassOop k = SystemDictionary::AdapterMethodHandle_klass(); + if (k != NULL && EnableMethodHandles) { + compute_offset(_conversion_offset, k, vmSymbols::conversion_name(), vmSymbols::int_signature(), true); + } +} + +oop java_dyn_MethodHandle::type(oop mh) { + return mh->obj_field(_type_offset); +} + +void java_dyn_MethodHandle::set_type(oop mh, oop mtype) { + mh->obj_field_put(_type_offset, mtype); +} + +int java_dyn_MethodHandle::vmslots(oop mh) { + int vmslots_offset = _vmslots_offset; + if (vmslots_offset != 0) { +#ifdef ASSERT + int x = mh->int_field(vmslots_offset); + int y = compute_vmslots(mh); + assert(x == y, "correct hoisted value"); +#endif + return mh->int_field(vmslots_offset); + } else { + return compute_vmslots(mh); + } +} + +// if MH.vmslots exists, hoist into it the value of type.form.vmslots +void java_dyn_MethodHandle::init_vmslots(oop mh) { + int vmslots_offset = _vmslots_offset; + if (vmslots_offset != 0) { + mh->int_field_put(vmslots_offset, compute_vmslots(mh)); + } +} + +// fetch type.form.vmslots, which is the number of JVM stack slots +// required to carry the arguments of this MH +int java_dyn_MethodHandle::compute_vmslots(oop mh) { + oop mtype = type(mh); + if (mtype == NULL) return 0; // Java code would get NPE + oop form = java_dyn_MethodType::form(mtype); + if (form == NULL) return 0; // Java code would get NPE + return java_dyn_MethodTypeForm::vmslots(form); +} + +// fetch the low-level entry point for this mh +MethodHandleEntry* java_dyn_MethodHandle::vmentry(oop mh) { + return (MethodHandleEntry*) mh->address_field(_vmentry_offset); +} + +void java_dyn_MethodHandle::set_vmentry(oop mh, MethodHandleEntry* me) { + assert(_vmentry_offset != 0, "must be present"); + + // This is always the final step that initializes a valid method handle: + mh->release_address_field_put(_vmentry_offset, (address) me); + + // There should be enough memory barriers on exit from native methods + // to ensure that the MH is fully initialized to all threads before + // Java code can publish it in global data structures. + // But just in case, we use release_address_field_put. +} + +/// MemberName accessors + +oop sun_dyn_MemberName::clazz(oop mname) { + assert(is_instance(mname), "wrong type"); + return mname->obj_field(_clazz_offset); +} + +void sun_dyn_MemberName::set_clazz(oop mname, oop clazz) { + assert(is_instance(mname), "wrong type"); + mname->obj_field_put(_clazz_offset, clazz); +} + +oop sun_dyn_MemberName::name(oop mname) { + assert(is_instance(mname), "wrong type"); + return mname->obj_field(_name_offset); +} + +void sun_dyn_MemberName::set_name(oop mname, oop name) { + assert(is_instance(mname), "wrong type"); + mname->obj_field_put(_name_offset, name); +} + +oop sun_dyn_MemberName::type(oop mname) { + assert(is_instance(mname), "wrong type"); + return mname->obj_field(_type_offset); +} + +void sun_dyn_MemberName::set_type(oop mname, oop type) { + assert(is_instance(mname), "wrong type"); + mname->obj_field_put(_type_offset, type); +} + +int sun_dyn_MemberName::flags(oop mname) { + assert(is_instance(mname), "wrong type"); + return mname->int_field(_flags_offset); +} + +void sun_dyn_MemberName::set_flags(oop mname, int flags) { + assert(is_instance(mname), "wrong type"); + mname->int_field_put(_flags_offset, flags); +} + +oop sun_dyn_MemberName::vmtarget(oop mname) { + assert(is_instance(mname), "wrong type"); + return mname->obj_field(_vmtarget_offset); +} + +void sun_dyn_MemberName::set_vmtarget(oop mname, oop ref) { + assert(is_instance(mname), "wrong type"); + mname->obj_field_put(_vmtarget_offset, ref); +} + +int sun_dyn_MemberName::vmindex(oop mname) { + assert(is_instance(mname), "wrong type"); + return mname->int_field(_vmindex_offset); +} + +void sun_dyn_MemberName::set_vmindex(oop mname, int index) { + assert(is_instance(mname), "wrong type"); + mname->int_field_put(_vmindex_offset, index); +} + +oop java_dyn_MethodHandle::vmtarget(oop mh) { + assert(is_instance(mh), "MH only"); + return mh->obj_field(_vmtarget_offset); +} + +void java_dyn_MethodHandle::set_vmtarget(oop mh, oop ref) { + assert(is_instance(mh), "MH only"); + mh->obj_field_put(_vmtarget_offset, ref); +} + +int sun_dyn_DirectMethodHandle::vmindex(oop mh) { + assert(is_instance(mh), "DMH only"); + return mh->int_field(_vmindex_offset); +} + +void sun_dyn_DirectMethodHandle::set_vmindex(oop mh, int index) { + assert(is_instance(mh), "DMH only"); + mh->int_field_put(_vmindex_offset, index); +} + +int sun_dyn_BoundMethodHandle::vmargslot(oop mh) { + assert(is_instance(mh), "BMH only"); + return mh->int_field(_vmargslot_offset); +} + +oop sun_dyn_BoundMethodHandle::argument(oop mh) { + assert(is_instance(mh), "BMH only"); + return mh->obj_field(_argument_offset); +} + +int sun_dyn_AdapterMethodHandle::conversion(oop mh) { + assert(is_instance(mh), "AMH only"); + return mh->int_field(_conversion_offset); +} + +void sun_dyn_AdapterMethodHandle::set_conversion(oop mh, int conv) { + assert(is_instance(mh), "AMH only"); + mh->int_field_put(_conversion_offset, conv); +} + + +// Support for java_dyn_MethodType + +int java_dyn_MethodType::_rtype_offset; +int java_dyn_MethodType::_ptypes_offset; +int java_dyn_MethodType::_form_offset; + +void java_dyn_MethodType::compute_offsets() { + klassOop k = SystemDictionary::MethodType_klass(); + if (k != NULL) { + compute_offset(_rtype_offset, k, vmSymbols::rtype_name(), vmSymbols::class_signature()); + compute_offset(_ptypes_offset, k, vmSymbols::ptypes_name(), vmSymbols::class_array_signature()); + compute_offset(_form_offset, k, vmSymbols::form_name(), vmSymbols::java_dyn_MethodTypeForm_signature()); + } +} + +void java_dyn_MethodType::print_signature(oop mt, outputStream* st) { + st->print("("); + objArrayOop pts = ptypes(mt); + for (int i = 0, limit = pts->length(); i < limit; i++) { + java_lang_Class::print_signature(pts->obj_at(i), st); + } + st->print(")"); + java_lang_Class::print_signature(rtype(mt), st); +} + +symbolOop java_dyn_MethodType::as_signature(oop mt, bool intern_if_not_found, TRAPS) { + ResourceMark rm; + stringStream buffer(128); + print_signature(mt, &buffer); + const char* sigstr = buffer.base(); + int siglen = (int) buffer.size(); + if (!intern_if_not_found) + return SymbolTable::probe(sigstr, siglen); + else + return oopFactory::new_symbol(sigstr, siglen, THREAD); +} + +oop java_dyn_MethodType::rtype(oop mt) { + assert(is_instance(mt), "must be a MethodType"); + return mt->obj_field(_rtype_offset); +} + +objArrayOop java_dyn_MethodType::ptypes(oop mt) { + assert(is_instance(mt), "must be a MethodType"); + return (objArrayOop) mt->obj_field(_ptypes_offset); +} + +oop java_dyn_MethodType::form(oop mt) { + assert(is_instance(mt), "must be a MethodType"); + return mt->obj_field(_form_offset); +} + +oop java_dyn_MethodType::ptype(oop mt, int idx) { + return ptypes(mt)->obj_at(idx); +} + + + +// Support for java_dyn_MethodTypeForm + +int java_dyn_MethodTypeForm::_vmslots_offset; +int java_dyn_MethodTypeForm::_erasedType_offset; + +void java_dyn_MethodTypeForm::compute_offsets() { + klassOop k = SystemDictionary::MethodTypeForm_klass(); + if (k != NULL) { + compute_optional_offset(_vmslots_offset, k, vmSymbols::vmslots_name(), vmSymbols::int_signature(), true); + compute_optional_offset(_erasedType_offset, k, vmSymbols::erasedType_name(), vmSymbols::java_dyn_MethodType_signature(), true); + } +} + +int java_dyn_MethodTypeForm::vmslots(oop mtform) { + assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only"); + return mtform->int_field(_vmslots_offset); +} + +oop java_dyn_MethodTypeForm::erasedType(oop mtform) { + assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only"); + return mtform->obj_field(_erasedType_offset); +} + + + + // Support for java_security_AccessControlContext int java_security_AccessControlContext::_context_offset = 0; int java_security_AccessControlContext::_privilegedContext_offset = 0; int java_security_AccessControlContext::_isPrivileged_offset = 0; - void java_security_AccessControlContext::compute_offsets() { assert(_isPrivileged_offset == 0, "offsets should be initialized only once"); fieldDescriptor fd; @@ -2442,6 +2766,15 @@ java_lang_System::compute_offsets(); java_lang_Thread::compute_offsets(); java_lang_ThreadGroup::compute_offsets(); + if (EnableMethodHandles) { + java_dyn_MethodHandle::compute_offsets(); + sun_dyn_MemberName::compute_offsets(); + sun_dyn_DirectMethodHandle::compute_offsets(); + sun_dyn_BoundMethodHandle::compute_offsets(); + sun_dyn_AdapterMethodHandle::compute_offsets(); + java_dyn_MethodType::compute_offsets(); + java_dyn_MethodTypeForm::compute_offsets(); + } java_security_AccessControlContext::compute_offsets(); // Initialize reflection classes. The layouts of these classes // changed with the new reflection implementation in JDK 1.4, and @@ -2459,6 +2792,9 @@ sun_reflect_UnsafeStaticFieldAccessorImpl::compute_offsets(); } sun_misc_AtomicLongCSImpl::compute_offsets(); + + // generated interpreter code wants to know about the offsets we just computed: + AbstractAssembler::update_delayed_values(); } #ifndef PRODUCT diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/classfile/javaClasses.hpp --- a/src/share/vm/classfile/javaClasses.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/classfile/javaClasses.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -151,6 +151,12 @@ // Conversion static klassOop as_klassOop(oop java_class); static BasicType as_BasicType(oop java_class, klassOop* reference_klass = NULL); + static BasicType as_BasicType(oop java_class, KlassHandle* reference_klass) { + klassOop refk_oop = NULL; + BasicType result = as_BasicType(java_class, &refk_oop); + (*reference_klass) = KlassHandle(refk_oop); + return result; + } static symbolOop as_signature(oop java_class, bool intern_if_not_found, TRAPS); static void print_signature(oop java_class, outputStream *st); // Testing @@ -778,6 +784,284 @@ }; +// Interface to java.dyn.MethodHandle objects + +class MethodHandleEntry; + +class java_dyn_MethodHandle: AllStatic { + friend class JavaClasses; + + private: + static int _vmentry_offset; // assembly code trampoline for MH + static int _vmtarget_offset; // class-specific target reference + static int _type_offset; // the MethodType of this MH + static int _vmslots_offset; // OPTIONAL hoisted type.form.vmslots + + static void compute_offsets(); + + public: + // Accessors + static oop type(oop mh); + static void set_type(oop mh, oop mtype); + + static oop vmtarget(oop mh); + static void set_vmtarget(oop mh, oop target); + + static MethodHandleEntry* vmentry(oop mh); + static void set_vmentry(oop mh, MethodHandleEntry* data); + + static int vmslots(oop mh); + static void init_vmslots(oop mh); + static int compute_vmslots(oop mh); + + // Testers + static bool is_subclass(klassOop klass) { + return Klass::cast(klass)->is_subclass_of(SystemDictionary::MethodHandle_klass()); + } + static bool is_instance(oop obj) { + return obj != NULL && is_subclass(obj->klass()); + } + + // Accessors for code generation: + static int type_offset_in_bytes() { return _type_offset; } + static int vmtarget_offset_in_bytes() { return _vmtarget_offset; } + static int vmentry_offset_in_bytes() { return _vmentry_offset; } + static int vmslots_offset_in_bytes() { return _vmslots_offset; } +}; + +class sun_dyn_DirectMethodHandle: public java_dyn_MethodHandle { + friend class JavaClasses; + + private: + // _vmtarget_offset; // method or class or interface + static int _vmindex_offset; // negative or vtable idx or itable idx + static void compute_offsets(); + + public: + // Accessors + static int vmindex(oop mh); + static void set_vmindex(oop mh, int index); + + // Testers + static bool is_subclass(klassOop klass) { + return Klass::cast(klass)->is_subclass_of(SystemDictionary::DirectMethodHandle_klass()); + } + static bool is_instance(oop obj) { + return obj != NULL && is_subclass(obj->klass()); + } + + // Accessors for code generation: + static int vmindex_offset_in_bytes() { return _vmindex_offset; } +}; + +class sun_dyn_BoundMethodHandle: public java_dyn_MethodHandle { + friend class JavaClasses; + + private: + static int _argument_offset; // argument value bound into this MH + static int _vmargslot_offset; // relevant argument slot (<= vmslots) + static void compute_offsets(); + +public: + static oop argument(oop mh); + static void set_argument(oop mh, oop ref); + + static jint vmargslot(oop mh); + static void set_vmargslot(oop mh, jint slot); + + // Testers + static bool is_subclass(klassOop klass) { + return Klass::cast(klass)->is_subclass_of(SystemDictionary::BoundMethodHandle_klass()); + } + static bool is_instance(oop obj) { + return obj != NULL && is_subclass(obj->klass()); + } + + static int argument_offset_in_bytes() { return _argument_offset; } + static int vmargslot_offset_in_bytes() { return _vmargslot_offset; } +}; + +class sun_dyn_AdapterMethodHandle: public sun_dyn_BoundMethodHandle { + friend class JavaClasses; + + private: + static int _conversion_offset; // type of conversion to apply + static void compute_offsets(); + + public: + static int conversion(oop mh); + static void set_conversion(oop mh, int conv); + + // Testers + static bool is_subclass(klassOop klass) { + return Klass::cast(klass)->is_subclass_of(SystemDictionary::AdapterMethodHandle_klass()); + } + static bool is_instance(oop obj) { + return obj != NULL && is_subclass(obj->klass()); + } + + // Relevant integer codes (keep these in synch. with MethodHandleNatives.Constants): + enum { + OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype + OP_CHECK_CAST = 0x1, // ref-to-ref conversion; requires a Class argument + OP_PRIM_TO_PRIM = 0x2, // converts from one primitive to another + OP_REF_TO_PRIM = 0x3, // unboxes a wrapper to produce a primitive + OP_PRIM_TO_REF = 0x4, // boxes a primitive into a wrapper (NYI) + OP_SWAP_ARGS = 0x5, // swap arguments (vminfo is 2nd arg) + OP_ROT_ARGS = 0x6, // rotate arguments (vminfo is displaced arg) + OP_DUP_ARGS = 0x7, // duplicates one or more arguments (at TOS) + OP_DROP_ARGS = 0x8, // remove one or more argument slots + OP_COLLECT_ARGS = 0x9, // combine one or more arguments into a varargs (NYI) + OP_SPREAD_ARGS = 0xA, // expand in place a varargs array (of known size) + OP_FLYBY = 0xB, // operate first on reified argument list (NYI) + OP_RICOCHET = 0xC, // run an adapter chain on the return value (NYI) + CONV_OP_LIMIT = 0xD, // limit of CONV_OP enumeration + + CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field + CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use + CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK + CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK + CONV_DEST_TYPE_SHIFT = 12, // byte 2 has the adapter BasicType (if needed) + CONV_SRC_TYPE_SHIFT = 16, // byte 2 has the source BasicType (if needed) + CONV_STACK_MOVE_SHIFT = 20, // high 12 bits give signed SP change + CONV_STACK_MOVE_MASK = (1 << (32 - CONV_STACK_MOVE_SHIFT)) - 1 + }; + + static int conversion_offset_in_bytes() { return _conversion_offset; } +}; + + +// Interface to sun.dyn.MemberName objects +// (These are a private interface for Java code to query the class hierarchy.) + +class sun_dyn_MemberName: AllStatic { + friend class JavaClasses; + + private: + // From java.dyn.MemberName: + // private Class clazz; // class in which the method is defined + // private String name; // may be null if not yet materialized + // private Object type; // may be null if not yet materialized + // private int flags; // modifier bits; see reflect.Modifier + // private Object vmtarget; // VM-specific target value + // private int vmindex; // method index within class or interface + static int _clazz_offset; + static int _name_offset; + static int _type_offset; + static int _flags_offset; + static int _vmtarget_offset; + static int _vmindex_offset; + + static void compute_offsets(); + + public: + // Accessors + static oop clazz(oop mname); + static void set_clazz(oop mname, oop clazz); + + static oop type(oop mname); + static void set_type(oop mname, oop type); + + static oop name(oop mname); + static void set_name(oop mname, oop name); + + static int flags(oop mname); + static void set_flags(oop mname, int flags); + + static int modifiers(oop mname) { return (u2) flags(mname); } + static void set_modifiers(oop mname, int mods) + { set_flags(mname, (flags(mname) &~ (u2)-1) | (u2)mods); } + + static oop vmtarget(oop mname); + static void set_vmtarget(oop mname, oop target); + + static int vmindex(oop mname); + static void set_vmindex(oop mname, int index); + + // Testers + static bool is_subclass(klassOop klass) { + return Klass::cast(klass)->is_subclass_of(SystemDictionary::MemberName_klass()); + } + static bool is_instance(oop obj) { + return obj != NULL && is_subclass(obj->klass()); + } + + // Relevant integer codes (keep these in synch. with MethodHandleNatives.Constants): + enum { + MN_IS_METHOD = 0x00010000, // method (not constructor) + MN_IS_CONSTRUCTOR = 0x00020000, // constructor + MN_IS_FIELD = 0x00040000, // field + MN_IS_TYPE = 0x00080000, // nested type + MN_SEARCH_SUPERCLASSES = 0x00100000, // for MHN.getMembers + MN_SEARCH_INTERFACES = 0x00200000, // for MHN.getMembers + VM_INDEX_UNINITIALIZED = -99 + }; + + // Accessors for code generation: + static int clazz_offset_in_bytes() { return _clazz_offset; } + static int type_offset_in_bytes() { return _type_offset; } + static int name_offset_in_bytes() { return _name_offset; } + static int flags_offset_in_bytes() { return _flags_offset; } + static int vmtarget_offset_in_bytes() { return _vmtarget_offset; } + static int vmindex_offset_in_bytes() { return _vmindex_offset; } +}; + + +// Interface to java.dyn.MethodType objects + +class java_dyn_MethodType: AllStatic { + friend class JavaClasses; + + private: + static int _rtype_offset; + static int _ptypes_offset; + static int _form_offset; + + static void compute_offsets(); + + public: + // Accessors + static oop rtype(oop mt); + static objArrayOop ptypes(oop mt); + static oop form(oop mt); + + static oop ptype(oop mt, int index); + + static symbolOop as_signature(oop mt, bool intern_if_not_found, TRAPS); + static void print_signature(oop mt, outputStream* st); + + static bool is_instance(oop obj) { + return obj != NULL && obj->klass() == SystemDictionary::MethodType_klass(); + } + + // Accessors for code generation: + static int rtype_offset_in_bytes() { return _rtype_offset; } + static int ptypes_offset_in_bytes() { return _ptypes_offset; } + static int form_offset_in_bytes() { return _form_offset; } +}; + +class java_dyn_MethodTypeForm: AllStatic { + friend class JavaClasses; + + private: + static int _vmslots_offset; // number of argument slots needed + static int _erasedType_offset; // erasedType = canonical MethodType + + static void compute_offsets(); + + public: + // Accessors + static int vmslots(oop mtform); + static oop erasedType(oop mtform); + + // Accessors for code generation: + static int vmslots_offset_in_bytes() { return _vmslots_offset; } + static int erasedType_offset_in_bytes() { return _erasedType_offset; } +}; + + + + // Interface to java.security.AccessControlContext objects class java_security_AccessControlContext: AllStatic { diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/classfile/systemDictionary.cpp --- a/src/share/vm/classfile/systemDictionary.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/classfile/systemDictionary.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -31,6 +31,7 @@ Dictionary* SystemDictionary::_shared_dictionary = NULL; LoaderConstraintTable* SystemDictionary::_loader_constraints = NULL; ResolutionErrorTable* SystemDictionary::_resolution_errors = NULL; +SymbolPropertyTable* SystemDictionary::_invoke_method_table = NULL; int SystemDictionary::_number_of_modifications = 0; @@ -966,6 +967,8 @@ instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, class_loader, protection_domain, + host_klass, + cp_patches, parsed_name, THREAD); @@ -1691,6 +1694,10 @@ // represent classes we're actively loading. placeholders_do(blk); + // Visit extra methods + if (invoke_method_table() != NULL) + invoke_method_table()->oops_do(blk); + // Loader constraints. We must keep the symbolOop used in the name alive. constraints()->always_strong_classes_do(blk); @@ -1726,6 +1733,10 @@ // Adjust dictionary dictionary()->oops_do(f); + // Visit extra methods + if (invoke_method_table() != NULL) + invoke_method_table()->oops_do(f); + // Partially loaded classes placeholders()->oops_do(f); @@ -1798,6 +1809,8 @@ void SystemDictionary::methods_do(void f(methodOop)) { dictionary()->methods_do(f); + if (invoke_method_table() != NULL) + invoke_method_table()->methods_do(f); } // ---------------------------------------------------------------------------- @@ -1830,6 +1843,7 @@ _number_of_modifications = 0; _loader_constraints = new LoaderConstraintTable(_loader_constraint_size); _resolution_errors = new ResolutionErrorTable(_resolution_error_size); + // _invoke_method_table is allocated lazily in find_method_handle_invoke() // Allocate private object used as system class loader lock _system_loader_lock_obj = oopFactory::new_system_objArray(0, CHECK); @@ -1891,6 +1905,9 @@ wk_klass_name_limits[0] = s; } } + + // move the starting value forward to the limit: + start_id = limit_id; } @@ -1924,6 +1941,17 @@ instanceKlass::cast(WK_KLASS(final_reference_klass))->set_reference_type(REF_FINAL); instanceKlass::cast(WK_KLASS(phantom_reference_klass))->set_reference_type(REF_PHANTOM); + WKID meth_group_start = WK_KLASS_ENUM_NAME(MethodHandle_klass); + WKID meth_group_end = WK_KLASS_ENUM_NAME(WrongMethodTypeException_klass); + initialize_wk_klasses_until(meth_group_start, scan, CHECK); + if (EnableMethodHandles) { + initialize_wk_klasses_through(meth_group_start, scan, CHECK); + } + if (_well_known_klasses[meth_group_start] == NULL) { + // Skip the rest of the method handle classes, if MethodHandle is not loaded. + scan = WKID(meth_group_end+1); + } + initialize_wk_klasses_until(WKID_LIMIT, scan, CHECK); _box_klasses[T_BOOLEAN] = WK_KLASS(boolean_klass); @@ -2254,6 +2282,91 @@ } +methodOop SystemDictionary::find_method_handle_invoke(symbolHandle signature, + Handle class_loader, + Handle protection_domain, + TRAPS) { + if (!EnableMethodHandles) return NULL; + assert(class_loader.is_null() && protection_domain.is_null(), + "cannot load specialized versions of MethodHandle.invoke"); + if (invoke_method_table() == NULL) { + // create this side table lazily + _invoke_method_table = new SymbolPropertyTable(_invoke_method_size); + } + unsigned int hash = invoke_method_table()->compute_hash(signature); + int index = invoke_method_table()->hash_to_index(hash); + SymbolPropertyEntry* spe = invoke_method_table()->find_entry(index, hash, signature); + if (spe == NULL || spe->property_oop() == NULL) { + // Must create lots of stuff here, but outside of the SystemDictionary lock. + Handle mt = compute_method_handle_type(signature(), + class_loader, protection_domain, + CHECK_NULL); + KlassHandle mh_klass = SystemDictionaryHandles::MethodHandle_klass(); + methodHandle m = methodOopDesc::make_invoke_method(mh_klass, signature, + mt, CHECK_NULL); + // Now grab the lock. We might have to throw away the new method, + // if a racing thread has managed to install one at the same time. + { + MutexLocker ml(SystemDictionary_lock, Thread::current()); + spe = invoke_method_table()->find_entry(index, hash, signature); + if (spe == NULL) + spe = invoke_method_table()->add_entry(index, hash, signature); + if (spe->property_oop() == NULL) + spe->set_property_oop(m()); + } + } + methodOop m = (methodOop) spe->property_oop(); + assert(m->is_method(), ""); + return m; +} + +// Ask Java code to find or construct a java.dyn.MethodType for the given +// signature, as interpreted relative to the given class loader. +// Because of class loader constraints, all method handle usage must be +// consistent with this loader. +Handle SystemDictionary::compute_method_handle_type(symbolHandle signature, + Handle class_loader, + Handle protection_domain, + TRAPS) { + Handle empty; + int npts = ArgumentCount(signature()).size(); + objArrayHandle pts = oopFactory::new_objArray(SystemDictionary::class_klass(), npts, CHECK_(empty)); + int arg = 0; + Handle rt; // the return type from the signature + for (SignatureStream ss(signature()); !ss.is_done(); ss.next()) { + oop mirror; + if (!ss.is_object()) { + mirror = Universe::java_mirror(ss.type()); + } else { + symbolOop name_oop = ss.as_symbol(CHECK_(empty)); + symbolHandle name(THREAD, name_oop); + klassOop klass = resolve_or_fail(name, + class_loader, protection_domain, + true, CHECK_(empty)); + mirror = Klass::cast(klass)->java_mirror(); + } + if (ss.at_return_type()) + rt = Handle(THREAD, mirror); + else + pts->obj_at_put(arg++, mirror); + } + assert(arg == npts, ""); + + // call MethodType java.dyn.MethodType::makeImpl(Class rt, Class[] pts, false, true) + bool varargs = false, trusted = true; + JavaCallArguments args(Handle(THREAD, rt())); + args.push_oop(pts()); + args.push_int(false); + args.push_int(trusted); + JavaValue result(T_OBJECT); + JavaCalls::call_static(&result, + SystemDictionary::MethodType_klass(), + vmSymbols::makeImpl_name(), vmSymbols::makeImpl_signature(), + &args, CHECK_(empty)); + return Handle(THREAD, (oop) result.get_jobject()); +} + + // Since the identity hash code for symbols changes when the symbols are // moved from the regular perm gen (hash in the mark word) to the shared // spaces (hash is the address), the classes loaded into the dictionary diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/classfile/systemDictionary.hpp --- a/src/share/vm/classfile/systemDictionary.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/classfile/systemDictionary.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -63,6 +63,7 @@ class LoaderConstraintTable; class HashtableBucket; class ResolutionErrorTable; +class SymbolPropertyTable; // Certain classes are preloaded, such as java.lang.Object and java.lang.String. // They are all "well-known", in the sense that no class loader is allowed @@ -131,6 +132,16 @@ template(reflect_constant_pool_klass, sun_reflect_ConstantPool, Opt_Only_JDK15) \ template(reflect_unsafe_static_field_accessor_impl_klass, sun_reflect_UnsafeStaticFieldAccessorImpl, Opt_Only_JDK15) \ \ + /* support for dynamic typing; it's OK if these are NULL in earlier JDKs */ \ + template(MethodHandle_klass, java_dyn_MethodHandle, Opt) \ + template(MemberName_klass, sun_dyn_MemberName, Opt) \ + template(MethodHandleImpl_klass, sun_dyn_MethodHandleImpl, Opt) \ + template(AdapterMethodHandle_klass, sun_dyn_AdapterMethodHandle, Opt) \ + template(BoundMethodHandle_klass, sun_dyn_BoundMethodHandle, Opt) \ + template(DirectMethodHandle_klass, sun_dyn_DirectMethodHandle, Opt) \ + template(MethodType_klass, java_dyn_MethodType, Opt) \ + template(MethodTypeForm_klass, java_dyn_MethodTypeForm, Opt) \ + template(WrongMethodTypeException_klass, java_dyn_WrongMethodTypeException, Opt) \ template(vector_klass, java_util_Vector, Pre) \ template(hashtable_klass, java_util_Hashtable, Pre) \ template(stringBuffer_klass, java_lang_StringBuffer, Pre) \ @@ -444,6 +455,17 @@ static char* check_signature_loaders(symbolHandle signature, Handle loader1, Handle loader2, bool is_method, TRAPS); + // JSR 292 + // find the java.dyn.MethodHandles::invoke method for a given signature + static methodOop find_method_handle_invoke(symbolHandle signature, + Handle class_loader, + Handle protection_domain, + TRAPS); + // ask Java to compute the java.dyn.MethodType object for a given signature + static Handle compute_method_handle_type(symbolHandle signature, + Handle class_loader, + Handle protection_domain, + TRAPS); // Utility for printing loader "name" as part of tracing constraints static const char* loader_name(oop loader) { return ((loader) == NULL ? "" : @@ -460,6 +482,7 @@ enum Constants { _loader_constraint_size = 107, // number of entries in constraint table _resolution_error_size = 107, // number of entries in resolution error table + _invoke_method_size = 139, // number of entries in invoke method table _nof_buckets = 1009 // number of buckets in hash table }; @@ -489,6 +512,9 @@ // Resolution errors static ResolutionErrorTable* _resolution_errors; + // Invoke methods (JSR 292) + static SymbolPropertyTable* _invoke_method_table; + public: // for VM_CounterDecay iteration support friend class CounterDecay; @@ -506,6 +532,7 @@ static PlaceholderTable* placeholders() { return _placeholders; } static LoaderConstraintTable* constraints() { return _loader_constraints; } static ResolutionErrorTable* resolution_errors() { return _resolution_errors; } + static SymbolPropertyTable* invoke_method_table() { return _invoke_method_table; } // Basic loading operations static klassOop resolve_instance_class_or_null(symbolHandle class_name, Handle class_loader, Handle protection_domain, TRAPS); diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/classfile/vmSymbols.hpp --- a/src/share/vm/classfile/vmSymbols.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/classfile/vmSymbols.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -216,7 +216,26 @@ template(sun_reflect_UnsafeStaticFieldAccessorImpl, "sun/reflect/UnsafeStaticFieldAccessorImpl")\ template(base_name, "base") \ \ - /* common method names */ \ + /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \ + template(java_dyn_MethodHandle, "java/dyn/MethodHandle") \ + template(java_dyn_MethodType, "java/dyn/MethodType") \ + template(java_dyn_WrongMethodTypeException, "java/dyn/WrongMethodTypeException") \ + template(java_dyn_MethodType_signature, "Ljava/dyn/MethodType;") \ + template(java_dyn_MethodHandle_signature, "Ljava/dyn/MethodHandle;") \ + /* internal classes known only to the JVM: */ \ + template(java_dyn_MethodTypeForm, "java/dyn/MethodTypeForm") \ + template(java_dyn_MethodTypeForm_signature, "Ljava/dyn/MethodTypeForm;") \ + template(sun_dyn_MemberName, "sun/dyn/MemberName") \ + template(sun_dyn_MethodHandleImpl, "sun/dyn/MethodHandleImpl") \ + template(sun_dyn_AdapterMethodHandle, "sun/dyn/AdapterMethodHandle") \ + template(sun_dyn_BoundMethodHandle, "sun/dyn/BoundMethodHandle") \ + template(sun_dyn_DirectMethodHandle, "sun/dyn/DirectMethodHandle") \ + template(makeImpl_name, "makeImpl") /*MethodType::makeImpl*/ \ + template(makeImpl_signature, "(Ljava/lang/Class;[Ljava/lang/Class;ZZ)Ljava/dyn/MethodType;") \ + NOT_LP64( do_alias(machine_word_signature, int_signature) ) \ + LP64_ONLY( do_alias(machine_word_signature, long_signature) ) \ + \ + /* common method and field names */ \ template(object_initializer_name, "") \ template(class_initializer_name, "") \ template(println_name, "println") \ @@ -289,6 +308,21 @@ template(bitCount_name, "bitCount") \ template(profile_name, "profile") \ template(equals_name, "equals") \ + template(toString_name, "toString") \ + template(values_name, "values") \ + template(receiver_name, "receiver") \ + template(vmtarget_name, "vmtarget") \ + template(vmentry_name, "vmentry") \ + template(vmslots_name, "vmslots") \ + template(vmindex_name, "vmindex") \ + template(vmargslot_name, "vmargslot") \ + template(flags_name, "flags") \ + template(argument_name, "argument") \ + template(conversion_name, "conversion") \ + template(rtype_name, "rtype") \ + template(ptypes_name, "ptypes") \ + template(form_name, "form") \ + template(erasedType_name, "erasedType") \ \ /* non-intrinsic name/signature pairs: */ \ template(register_method_name, "register") \ @@ -353,6 +387,7 @@ template(void_classloader_signature, "()Ljava/lang/ClassLoader;") \ template(void_object_signature, "()Ljava/lang/Object;") \ template(void_class_signature, "()Ljava/lang/Class;") \ + template(void_string_signature, "()Ljava/lang/String;") \ template(object_array_object_object_signature, "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")\ template(exception_void_signature, "(Ljava/lang/Exception;)V") \ template(protectiondomain_signature, "[Ljava/security/ProtectionDomain;") \ diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/includeDB_core --- a/src/share/vm/includeDB_core Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/includeDB_core Wed Apr 08 10:56:49 2009 -0700 @@ -254,6 +254,7 @@ assembler_.cpp collectedHeap.inline.hpp assembler_.cpp interfaceSupport.hpp assembler_.cpp interpreter.hpp +assembler_.cpp methodHandles.hpp assembler_.cpp objectMonitor.hpp assembler_.cpp os.hpp assembler_.cpp resourceArea.hpp @@ -1274,6 +1275,7 @@ cpCacheKlass.cpp collectedHeap.hpp cpCacheKlass.cpp constantPoolOop.hpp cpCacheKlass.cpp cpCacheKlass.hpp +cpCacheKlass.cpp genOopClosures.inline.hpp cpCacheKlass.cpp handles.inline.hpp cpCacheKlass.cpp javaClasses.hpp cpCacheKlass.cpp markSweep.inline.hpp @@ -2202,6 +2204,7 @@ interpreter_.cpp jvmtiExport.hpp interpreter_.cpp jvmtiThreadState.hpp interpreter_.cpp methodDataOop.hpp +interpreter_.cpp methodHandles.hpp interpreter_.cpp methodOop.hpp interpreter_.cpp oop.inline.hpp interpreter_.cpp sharedRuntime.hpp @@ -2596,6 +2599,7 @@ linkResolver.cpp instanceKlass.hpp linkResolver.cpp interpreterRuntime.hpp linkResolver.cpp linkResolver.hpp +linkResolver.cpp methodHandles.hpp linkResolver.cpp nativeLookup.hpp linkResolver.cpp objArrayOop.hpp linkResolver.cpp reflection.hpp @@ -2812,6 +2816,25 @@ methodDataOop.hpp orderAccess.hpp methodDataOop.hpp universe.hpp +methodHandles.hpp frame.inline.hpp +methodHandles.hpp globals.hpp +methodHandles.hpp interfaceSupport.hpp +methodHandles.hpp javaClasses.hpp +methodHandles.hpp vmSymbols.hpp + +methodHandles.cpp allocation.inline.hpp +methodHandles.cpp interpreter.hpp +methodHandles.cpp javaCalls.hpp +methodHandles.cpp methodHandles.hpp +methodHandles.cpp oopFactory.hpp +methodHandles.cpp reflection.hpp +methodHandles.cpp signature.hpp +methodHandles.cpp symbolTable.hpp + +methodHandles_.cpp allocation.inline.hpp +methodHandles_.cpp interpreter.hpp +methodHandles_.cpp methodHandles.hpp + methodKlass.cpp collectedHeap.inline.hpp methodKlass.cpp constMethodKlass.hpp methodKlass.cpp gcLocker.hpp @@ -3061,6 +3084,7 @@ oop.inline.hpp arrayOop.hpp oop.inline.hpp atomic.hpp oop.inline.hpp barrierSet.inline.hpp +oop.inline.hpp bytes_.hpp oop.inline.hpp cardTableModRefBS.hpp oop.inline.hpp collectedHeap.inline.hpp oop.inline.hpp compactingPermGenGen.hpp @@ -3674,6 +3698,7 @@ sharedRuntime.cpp interpreter.hpp sharedRuntime.cpp javaCalls.hpp sharedRuntime.cpp jvmtiExport.hpp +sharedRuntime.cpp methodHandles.hpp sharedRuntime.cpp jvmtiRedefineClassesTrace.hpp sharedRuntime.cpp nativeInst_.hpp sharedRuntime.cpp nativeLookup.hpp @@ -3862,6 +3887,7 @@ stubGenerator_.cpp handles.inline.hpp stubGenerator_.cpp instanceOop.hpp stubGenerator_.cpp interpreter.hpp +stubGenerator_.cpp methodHandles.hpp stubGenerator_.cpp methodOop.hpp stubGenerator_.cpp nativeInst_.hpp stubGenerator_.cpp objArrayKlass.hpp diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/includeDB_gc_parallel --- a/src/share/vm/includeDB_gc_parallel Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/includeDB_gc_parallel Wed Apr 08 10:56:49 2009 -0700 @@ -36,6 +36,12 @@ constantPoolKlass.cpp psScavenge.inline.hpp constantPoolKlass.cpp parOopClosures.inline.hpp +constantPoolKlass.cpp cardTableRS.hpp +constantPoolKlass.cpp oop.pcgc.inline.hpp +constantPoolKlass.cpp psPromotionManager.inline.hpp +constantPoolKlass.cpp psScavenge.inline.hpp +constantPoolKlass.cpp parOopClosures.inline.hpp + genCollectedHeap.cpp concurrentMarkSweepThread.hpp genCollectedHeap.cpp vmCMSOperations.hpp diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/abstractInterpreter.hpp --- a/src/share/vm/interpreter/abstractInterpreter.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/abstractInterpreter.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,6 +61,7 @@ empty, // empty method (code: _return) accessor, // accessor method (code: _aload_0, _getfield, _(a|i)return) abstract, // abstract method (throws an AbstractMethodException) + method_handle, // java.dyn.MethodHandles::invoke java_lang_math_sin, // implementation of java.lang.Math.sin (x) java_lang_math_cos, // implementation of java.lang.Math.cos (x) java_lang_math_tan, // implementation of java.lang.Math.tan (x) @@ -91,8 +92,6 @@ static address _rethrow_exception_entry; // rethrows an activation in previous frame - - friend class AbstractInterpreterGenerator; friend class InterpreterGenerator; friend class InterpreterMacroAssembler; diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/cppInterpreter.cpp --- a/src/share/vm/interpreter/cppInterpreter.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/cppInterpreter.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -114,6 +114,7 @@ method_entry(empty); method_entry(accessor); method_entry(abstract); + method_entry(method_handle); method_entry(java_lang_math_sin ); method_entry(java_lang_math_cos ); method_entry(java_lang_math_tan ); diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/interpreter.cpp --- a/src/share/vm/interpreter/interpreter.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/interpreter.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -168,10 +168,14 @@ // Abstract method? if (m->is_abstract()) return abstract; + // Invoker for method handles? + if (m->is_method_handle_invoke()) return method_handle; + // Native method? // Note: This test must come _before_ the test for intrinsic // methods. See also comments below. if (m->is_native()) { + assert(!m->is_method_handle_invoke(), "overlapping bits here, watch out"); return m->is_synchronized() ? native_synchronized : native; } @@ -249,6 +253,7 @@ case empty : tty->print("empty" ); break; case accessor : tty->print("accessor" ); break; case abstract : tty->print("abstract" ); break; + case method_handle : tty->print("method_handle" ); break; case java_lang_math_sin : tty->print("java_lang_math_sin" ); break; case java_lang_math_cos : tty->print("java_lang_math_cos" ); break; case java_lang_math_tan : tty->print("java_lang_math_tan" ); break; diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/interpreterRuntime.cpp --- a/src/share/vm/interpreter/interpreterRuntime.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/interpreterRuntime.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -293,6 +293,24 @@ THROW_MSG(vmSymbols::java_lang_ClassCastException(), message); IRT_END +// required can be either a MethodType, or a Class (for a single argument) +// actual (if not null) can be either a MethodHandle, or an arbitrary value (for a single argument) +IRT_ENTRY(void, InterpreterRuntime::throw_WrongMethodTypeException(JavaThread* thread, + oopDesc* required, + oopDesc* actual)) { + ResourceMark rm(thread); + char* message = SharedRuntime::generate_wrong_method_type_message(thread, required, actual); + + if (ProfileTraps) { + note_trap(thread, Deoptimization::Reason_constraint, CHECK); + } + + // create exception + THROW_MSG(vmSymbols::java_dyn_WrongMethodTypeException(), message); +} +IRT_END + + // exception_handler_for_exception(...) returns the continuation address, // the exception oop (via TLS) and sets the bci/bcp for the continuation. diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/interpreterRuntime.hpp --- a/src/share/vm/interpreter/interpreterRuntime.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/interpreterRuntime.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,6 +66,7 @@ static void throw_StackOverflowError(JavaThread* thread); static void throw_ArrayIndexOutOfBoundsException(JavaThread* thread, char* name, jint index); static void throw_ClassCastException(JavaThread* thread, oopDesc* obj); + static void throw_WrongMethodTypeException(JavaThread* thread, oopDesc* mtype = NULL, oopDesc* mhandle = NULL); static void create_exception(JavaThread* thread, char* name, char* message); static void create_klass_exception(JavaThread* thread, char* name, oopDesc* obj); static address exception_handler_for_exception(JavaThread* thread, oopDesc* exception); diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/linkResolver.cpp --- a/src/share/vm/interpreter/linkResolver.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/linkResolver.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -151,6 +151,20 @@ result = methodHandle(THREAD, ik->lookup_method_in_all_interfaces(name(), signature())); } +void LinkResolver::lookup_implicit_method(methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS) { + if (EnableMethodHandles && MethodHandles::enabled() && + name == vmSymbolHandles::invoke_name() && klass() == SystemDictionary::MethodHandle_klass()) { + methodOop result_oop = SystemDictionary::find_method_handle_invoke(signature, + Handle(), + Handle(), + CHECK); + if (result_oop != NULL) { + assert(result_oop->is_method_handle_invoke() && result_oop->signature() == signature(), "consistent"); + result = methodHandle(THREAD, result_oop); + } + } +} + void LinkResolver::check_method_accessability(KlassHandle ref_klass, KlassHandle resolved_klass, KlassHandle sel_klass, @@ -240,6 +254,11 @@ lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK); if (resolved_method.is_null()) { + // JSR 292: see if this is an implicitly generated method MethodHandle.invoke(*...) + lookup_implicit_method(resolved_method, resolved_klass, method_name, method_signature, CHECK); + } + + if (resolved_method.is_null()) { // 4. method lookup failed ResourceMark rm(THREAD); THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(), diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/linkResolver.hpp --- a/src/share/vm/interpreter/linkResolver.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/linkResolver.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -103,6 +103,7 @@ static void lookup_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); static void lookup_instance_method_in_klasses (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); static void lookup_method_in_interfaces (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); + static void lookup_implicit_method (methodHandle& result, KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); static int vtable_index_of_miranda_method(KlassHandle klass, symbolHandle name, symbolHandle signature, TRAPS); diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/templateInterpreter.cpp --- a/src/share/vm/interpreter/templateInterpreter.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/templateInterpreter.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -168,6 +168,7 @@ address TemplateInterpreter::_throw_ArrayStoreException_entry = NULL; address TemplateInterpreter::_throw_ArithmeticException_entry = NULL; address TemplateInterpreter::_throw_ClassCastException_entry = NULL; +address TemplateInterpreter::_throw_WrongMethodType_entry = NULL; address TemplateInterpreter::_throw_NullPointerException_entry = NULL; address TemplateInterpreter::_throw_StackOverflowError_entry = NULL; address TemplateInterpreter::_throw_exception_entry = NULL; @@ -341,6 +342,7 @@ Interpreter::_throw_ArrayStoreException_entry = generate_klass_exception_handler("java/lang/ArrayStoreException" ); Interpreter::_throw_ArithmeticException_entry = generate_exception_handler("java/lang/ArithmeticException" , "/ by zero"); Interpreter::_throw_ClassCastException_entry = generate_ClassCastException_handler(); + Interpreter::_throw_WrongMethodType_entry = generate_WrongMethodType_handler(); Interpreter::_throw_NullPointerException_entry = generate_exception_handler("java/lang/NullPointerException" , NULL ); Interpreter::_throw_StackOverflowError_entry = generate_StackOverflowError_handler(); } @@ -358,6 +360,7 @@ method_entry(empty) method_entry(accessor) method_entry(abstract) + method_entry(method_handle) method_entry(java_lang_math_sin ) method_entry(java_lang_math_cos ) method_entry(java_lang_math_tan ) diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/templateInterpreter.hpp --- a/src/share/vm/interpreter/templateInterpreter.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/templateInterpreter.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -77,6 +77,7 @@ friend class VMStructs; friend class InterpreterMacroAssembler; friend class TemplateInterpreterGenerator; + friend class InterpreterGenerator; friend class TemplateTable; // friend class Interpreter; public: @@ -93,6 +94,7 @@ static address _throw_ArrayStoreException_entry; static address _throw_ArithmeticException_entry; static address _throw_ClassCastException_entry; + static address _throw_WrongMethodType_entry; static address _throw_NullPointerException_entry; static address _throw_exception_entry; @@ -137,6 +139,7 @@ static address remove_activation_entry() { return _remove_activation_entry; } static address throw_exception_entry() { return _throw_exception_entry; } static address throw_ArithmeticException_entry() { return _throw_ArithmeticException_entry; } + static address throw_WrongMethodType_entry() { return _throw_WrongMethodType_entry; } static address throw_NullPointerException_entry() { return _throw_NullPointerException_entry; } static address throw_StackOverflowError_entry() { return _throw_StackOverflowError_entry; } diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/interpreter/templateInterpreterGenerator.hpp --- a/src/share/vm/interpreter/templateInterpreterGenerator.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/interpreter/templateInterpreterGenerator.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,6 +48,7 @@ } address generate_exception_handler_common(const char* name, const char* message, bool pass_oop); address generate_ClassCastException_handler(); + address generate_WrongMethodType_handler(); address generate_ArrayIndexOutOfBounds_handler(const char* name); address generate_continuation_for(TosState state); address generate_return_entry_for(TosState state, int step); diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/memory/dump.cpp --- a/src/share/vm/memory/dump.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/memory/dump.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -928,6 +928,9 @@ // shared classes at runtime, where constraints were previously created. guarantee(SystemDictionary::constraints()->number_of_entries() == 0, "loader constraints are not saved"); + // Revisit and implement this if we prelink method handle call sites: + guarantee(SystemDictionary::invoke_method_table()->number_of_entries() == 0, + "invoke method table is not saved"); GenCollectedHeap* gch = GenCollectedHeap::heap(); // At this point, many classes have been loaded. diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/oops/methodKlass.cpp --- a/src/share/vm/oops/methodKlass.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/oops/methodKlass.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -298,7 +298,11 @@ m->code()->print_value_on(st); st->cr(); } - if (m->is_native()) { + if (m->is_method_handle_invoke()) { + st->print_cr(" - invoke method type: " INTPTR_FORMAT, (address) m->method_handle_type()); + // m is classified as native, but it does not have an interesting + // native_function or signature handler + } else if (m->is_native()) { st->print_cr(" - native function: " INTPTR_FORMAT, m->native_function()); st->print_cr(" - signature handler: " INTPTR_FORMAT, m->signature_handler()); } diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/oops/methodOop.cpp --- a/src/share/vm/oops/methodOop.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/oops/methodOop.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -304,6 +304,12 @@ } +int methodOopDesc::extra_stack_words() { + // not an inline function, to avoid a header dependency on Interpreter + return extra_stack_entries() * Interpreter::stackElementSize(); +} + + void methodOopDesc::compute_size_of_parameters(Thread *thread) { symbolHandle h_signature(thread, signature()); ArgumentSizeComputer asc(h_signature); @@ -564,6 +570,11 @@ bool methodOopDesc::is_not_compilable(int comp_level) const { + if (is_method_handle_invoke()) { + // compilers must recognize this method specially, or not at all + return true; + } + methodDataOop mdo = method_data(); if (mdo != NULL && (uint)mdo->decompile_count() > (uint)PerMethodRecompilationCutoff) { @@ -651,7 +662,7 @@ assert(entry != NULL, "interpreter entry must be non-null"); // Sets both _i2i_entry and _from_interpreted_entry set_interpreter_entry(entry); - if (is_native()) { + if (is_native() && !is_method_handle_invoke()) { set_native_function( SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), !native_bind_event_is_interesting); @@ -783,6 +794,100 @@ return false; } +// Constant pool structure for invoke methods: +enum { + _imcp_invoke_name = 1, // utf8: 'invoke' + _imcp_invoke_signature, // utf8: (variable symbolOop) + _imcp_method_type_value, // string: (variable java/dyn/MethodType, sic) + _imcp_limit +}; + +oop methodOopDesc::method_handle_type() const { + if (!is_method_handle_invoke()) { assert(false, "caller resp."); return NULL; } + oop mt = constants()->resolved_string_at(_imcp_method_type_value); + assert(mt->klass() == SystemDictionary::MethodType_klass(), ""); + return mt; +} + +jint* methodOopDesc::method_type_offsets_chain() { + static jint pchase[] = { -1, -1, -1 }; + if (pchase[0] == -1) { + jint step0 = in_bytes(constants_offset()); + jint step1 = (constantPoolOopDesc::header_size() + _imcp_method_type_value) * HeapWordSize; + // do this in reverse to avoid races: + OrderAccess::release_store(&pchase[1], step1); + OrderAccess::release_store(&pchase[0], step0); + } + return pchase; +} + +methodHandle methodOopDesc::make_invoke_method(KlassHandle holder, + symbolHandle signature, + Handle method_type, TRAPS) { + methodHandle empty; + + assert(holder() == SystemDictionary::MethodHandle_klass(), + "must be a JSR 292 magic type"); + + if (TraceMethodHandles) { + tty->print("Creating invoke method for "); + signature->print_value(); + tty->cr(); + } + + constantPoolHandle cp; + { + constantPoolOop cp_oop = oopFactory::new_constantPool(_imcp_limit, IsSafeConc, CHECK_(empty)); + cp = constantPoolHandle(THREAD, cp_oop); + } + cp->symbol_at_put(_imcp_invoke_name, vmSymbols::invoke_name()); + cp->symbol_at_put(_imcp_invoke_signature, signature()); + cp->string_at_put(_imcp_method_type_value, vmSymbols::void_signature()); + cp->set_pool_holder(holder()); + + // set up the fancy stuff: + cp->pseudo_string_at_put(_imcp_method_type_value, method_type()); + methodHandle m; + { + int flags_bits = (JVM_MH_INVOKE_BITS | JVM_ACC_PUBLIC | JVM_ACC_FINAL); + methodOop m_oop = oopFactory::new_method(0, accessFlags_from(flags_bits), + 0, 0, 0, IsSafeConc, CHECK_(empty)); + m = methodHandle(THREAD, m_oop); + } + m->set_constants(cp()); + m->set_name_index(_imcp_invoke_name); + m->set_signature_index(_imcp_invoke_signature); + assert(m->name() == vmSymbols::invoke_name(), ""); + assert(m->signature() == signature(), ""); +#ifdef CC_INTERP + ResultTypeFinder rtf(signature()); + m->set_result_index(rtf.type()); +#endif + m->compute_size_of_parameters(THREAD); + m->set_exception_table(Universe::the_empty_int_array()); + + // Finally, set up its entry points. + assert(m->method_handle_type() == method_type(), ""); + assert(m->can_be_statically_bound(), ""); + m->set_vtable_index(methodOopDesc::nonvirtual_vtable_index); + m->link_method(m, CHECK_(empty)); + +#ifdef ASSERT + // Make sure the pointer chase works. + address p = (address) m(); + for (jint* pchase = method_type_offsets_chain(); (*pchase) != -1; pchase++) { + p = *(address*)(p + (*pchase)); + } + assert((oop)p == method_type(), "pointer chase is correct"); +#endif + + if (TraceMethodHandles) + m->print_on(tty); + + return m; +} + + methodHandle methodOopDesc:: clone_with_new_data(methodHandle m, u_char* new_code, int new_code_length, u_char* new_compressed_linenumber_table, int new_compressed_linenumber_size, TRAPS) { diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/oops/methodOop.hpp --- a/src/share/vm/oops/methodOop.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/oops/methodOop.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -320,6 +320,7 @@ enum VtableIndexFlag { // Valid vtable indexes are non-negative (>= 0). // These few negative values are used as sentinels. + highest_unused_vtable_index_value = -5, invalid_vtable_index = -4, // distinct from any valid vtable index garbage_vtable_index = -3, // not yet linked; no vtable layout yet nonvirtual_vtable_index = -2 // there is no need for vtable dispatch @@ -523,6 +524,18 @@ // Reflection support bool is_overridden_in(klassOop k) const; + // JSR 292 support + bool is_method_handle_invoke() const { return access_flags().is_method_handle_invoke(); } + static methodHandle make_invoke_method(KlassHandle holder, + symbolHandle signature, + Handle method_type, + TRAPS); + // these operate only on invoke methods: + oop method_handle_type() const; + static jint* method_type_offsets_chain(); // series of pointer-offsets, terminated by -1 + // presize interpreter frames for extra interpreter stack entries, if needed + static int extra_stack_entries() { return EnableMethodHandles ? (int)MethodHandlePushLimit : 0; } + static int extra_stack_words(); // = extra_stack_entries() * Interpreter::stackElementSize() // RedefineClasses() support: bool is_old() const { return access_flags().is_old(); } void set_is_old() { _access_flags.set_is_old(); } diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/oops/oop.hpp --- a/src/share/vm/oops/oop.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/oops/oop.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -263,6 +263,9 @@ jdouble double_field_acquire(int offset) const; void release_double_field_put(int offset, jdouble contents); + address address_field_acquire(int offset) const; + void release_address_field_put(int offset, address contents); + // printing functions for VM debugging void print_on(outputStream* st) const; // First level print void print_value_on(outputStream* st) const; // Second level print. diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/oops/oop.inline.hpp --- a/src/share/vm/oops/oop.inline.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/oops/oop.inline.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -349,6 +349,9 @@ inline jdouble oopDesc::double_field_acquire(int offset) const { return OrderAccess::load_acquire(double_field_addr(offset)); } inline void oopDesc::release_double_field_put(int offset, jdouble contents) { OrderAccess::release_store(double_field_addr(offset), contents); } +inline address oopDesc::address_field_acquire(int offset) const { return (address) OrderAccess::load_ptr_acquire(address_field_addr(offset)); } +inline void oopDesc::release_address_field_put(int offset, address contents) { OrderAccess::release_store_ptr(address_field_addr(offset), contents); } + inline int oopDesc::size_given_klass(Klass* klass) { int lh = klass->layout_helper(); int s = lh >> LogHeapWordSize; // deliver size scaled by wordSize diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/prims/methodHandles.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/prims/methodHandles.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -0,0 +1,2347 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* + * JSR 292 reference implementation: method handles + */ + +#include "incls/_precompiled.incl" +#include "incls/_methodHandles.cpp.incl" + +bool MethodHandles::_enabled = false; // set true after successful native linkage + +MethodHandleEntry* MethodHandles::_entries[MethodHandles::_EK_LIMIT] = {NULL}; +const char* MethodHandles::_entry_names[_EK_LIMIT+1] = { + "check_mtype", + "wrong_method_type", // what happens when there is a type mismatch + "invokestatic", // how a MH emulates invokestatic + "invokespecial", // ditto for the other invokes... + "invokevirtual", + "invokeinterface", + "bound_ref", // these are for BMH... + "bound_int", + "bound_long", + "bound_ref_direct", // (direct versions have a direct methodOop) + "bound_int_direct", + "bound_long_direct", + + // starting at _adapter_mh_first: + "adapter_retype_only", // these are for AMH... + "adapter_check_cast", + "adapter_prim_to_prim", + "adapter_ref_to_prim", + "adapter_prim_to_ref", + "adapter_swap_args", + "adapter_rot_args", + "adapter_dup_args", + "adapter_drop_args", + "adapter_collect_args", + "adapter_spread_args", + "adapter_flyby", + "adapter_ricochet", + + // optimized adapter types: + "adapter_swap_args/1", + "adapter_swap_args/2", + "adapter_rot_args/1,up", + "adapter_rot_args/1,down", + "adapter_rot_args/2,up", + "adapter_rot_args/2,down", + "adapter_prim_to_prim/i2i", + "adapter_prim_to_prim/l2i", + "adapter_prim_to_prim/d2f", + "adapter_prim_to_prim/i2l", + "adapter_prim_to_prim/f2d", + "adapter_ref_to_prim/unboxi", + "adapter_ref_to_prim/unboxl", + "adapter_spread_args/0", + "adapter_spread_args/1", + "adapter_spread_args/more", + + NULL +}; + +#ifdef ASSERT +bool MethodHandles::spot_check_entry_names() { + assert(!strcmp(entry_name(_invokestatic_mh), "invokestatic"), ""); + assert(!strcmp(entry_name(_bound_ref_mh), "bound_ref"), ""); + assert(!strcmp(entry_name(_adapter_retype_only), "adapter_retype_only"), ""); + assert(!strcmp(entry_name(_adapter_ricochet), "adapter_ricochet"), ""); + assert(!strcmp(entry_name(_adapter_opt_unboxi), "adapter_ref_to_prim/unboxi"), ""); + return true; +} +#endif + +void MethodHandles::set_enabled(bool z) { + if (_enabled != z) { + guarantee(z && EnableMethodHandles, "can only enable once, and only if -XX:+EnableMethodHandles"); + _enabled = z; + } +} + +// Note: A method which does not have a TRAPS argument cannot block in the GC +// or throw exceptions. Such methods are used in this file to do something quick +// and local, like parse a data structure. For speed, such methods work on plain +// oops, not handles. Trapping methods uniformly operate on handles. + +methodOop MethodHandles::decode_vmtarget(oop vmtarget, int vmindex, oop mtype, + klassOop& receiver_limit_result, int& decode_flags_result) { + if (vmtarget == NULL) return NULL; + assert(methodOopDesc::nonvirtual_vtable_index < 0, "encoding"); + if (vmindex < 0) { + // this DMH performs no dispatch; it is directly bound to a methodOop + // A MemberName may either be directly bound to a methodOop, + // or it may use the klass/index form; both forms mean the same thing. + methodOop m = decode_methodOop(methodOop(vmtarget), decode_flags_result); + if ((decode_flags_result & _dmf_has_receiver) != 0 + && java_dyn_MethodType::is_instance(mtype)) { + // Extract receiver type restriction from mtype.ptypes[0]. + objArrayOop ptypes = java_dyn_MethodType::ptypes(mtype); + oop ptype0 = (ptypes == NULL || ptypes->length() < 1) ? oop(NULL) : ptypes->obj_at(0); + if (java_lang_Class::is_instance(ptype0)) + receiver_limit_result = java_lang_Class::as_klassOop(ptype0); + } + if (vmindex == methodOopDesc::nonvirtual_vtable_index) { + // this DMH can be an "invokespecial" version + decode_flags_result &= ~_dmf_does_dispatch; + } else { + assert(vmindex == methodOopDesc::invalid_vtable_index, "random vmindex?"); + } + return m; + } else { + decode_flags_result |= MethodHandles::_dmf_does_dispatch; + assert(vmtarget->is_klass(), "must be class or interface"); + receiver_limit_result = (klassOop)vmtarget; + Klass* tk = Klass::cast((klassOop)vmtarget); + if (tk->is_interface()) { + // an itable linkage is + decode_flags_result |= MethodHandles::_dmf_from_interface; + return klassItable::method_for_itable_index((klassOop)vmtarget, vmindex); + } else { + if (!tk->oop_is_instance()) + tk = instanceKlass::cast(SystemDictionary::object_klass()); + return ((instanceKlass*)tk)->method_at_vtable(vmindex); + } + } +} + +// MemberName and DirectMethodHandle have the same linkage to the JVM internals. +// (MemberName is the non-operational name used for queries and setup.) + +methodOop MethodHandles::decode_DirectMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result) { + oop vmtarget = sun_dyn_DirectMethodHandle::vmtarget(mh); + int vmindex = sun_dyn_DirectMethodHandle::vmindex(mh); + oop mtype = sun_dyn_DirectMethodHandle::type(mh); + return decode_vmtarget(vmtarget, vmindex, mtype, receiver_limit_result, decode_flags_result); +} + +methodOop MethodHandles::decode_BoundMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result) { + assert(mh->klass() == SystemDictionary::BoundMethodHandle_klass(), ""); + for (oop bmh = mh;;) { + // Bound MHs can be stacked to bind several arguments. + oop target = java_dyn_MethodHandle::vmtarget(bmh); + if (target == NULL) return NULL; + decode_flags_result |= MethodHandles::_dmf_binds_argument; + klassOop tk = target->klass(); + if (tk == SystemDictionary::BoundMethodHandle_klass()) { + bmh = target; + continue; + } else { + if (java_dyn_MethodHandle::is_subclass(tk)) { + //assert(tk == SystemDictionary::DirectMethodHandle_klass(), "end of BMH chain must be DMH"); + return decode_MethodHandle(target, receiver_limit_result, decode_flags_result); + } else { + // Optimized case: binding a receiver to a non-dispatched DMH + // short-circuits directly to the methodOop. + assert(target->is_method(), "must be a simple method"); + methodOop m = (methodOop) target; + DEBUG_ONLY(int argslot = sun_dyn_BoundMethodHandle::vmargslot(bmh)); + assert(argslot == m->size_of_parameters() - 1, "must be initial argument (receiver)"); + decode_flags_result |= MethodHandles::_dmf_binds_method; + return m; + } + } + } +} + +methodOop MethodHandles::decode_AdapterMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result) { + assert(mh->klass() == SystemDictionary::AdapterMethodHandle_klass(), ""); + for (oop amh = mh;;) { + // Adapter MHs can be stacked to convert several arguments. + int conv_op = adapter_conversion_op(sun_dyn_AdapterMethodHandle::conversion(amh)); + decode_flags_result |= (_dmf_adapter_lsb << conv_op) & _DMF_ADAPTER_MASK; + oop target = java_dyn_MethodHandle::vmtarget(amh); + if (target == NULL) return NULL; + klassOop tk = target->klass(); + if (tk == SystemDictionary::AdapterMethodHandle_klass()) { + amh = target; + continue; + } else { + // must be a BMH (which will bind some more arguments) or a DMH (for the final call) + return MethodHandles::decode_MethodHandle(target, receiver_limit_result, decode_flags_result); + } + } +} + +methodOop MethodHandles::decode_MethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result) { + if (mh == NULL) return NULL; + klassOop mhk = mh->klass(); + assert(java_dyn_MethodHandle::is_subclass(mhk), "must be a MethodHandle"); + if (mhk == SystemDictionary::DirectMethodHandle_klass()) { + return decode_DirectMethodHandle(mh, receiver_limit_result, decode_flags_result); + } else if (mhk == SystemDictionary::BoundMethodHandle_klass()) { + return decode_BoundMethodHandle(mh, receiver_limit_result, decode_flags_result); + } else if (mhk == SystemDictionary::AdapterMethodHandle_klass()) { + return decode_AdapterMethodHandle(mh, receiver_limit_result, decode_flags_result); + } else { + assert(false, "cannot parse this MH"); + return NULL; // random MH? + } +} + +methodOop MethodHandles::decode_methodOop(methodOop m, int& decode_flags_result) { + assert(m->is_method(), ""); + if (m->is_static()) { + // check that signature begins '(L' or '([' (not '(I', '()', etc.) + symbolOop sig = m->signature(); + BasicType recv_bt = char2type(sig->byte_at(1)); + // Note: recv_bt might be T_ILLEGAL if byte_at(2) is ')' + assert(sig->byte_at(0) == '(', "must be method sig"); + if (recv_bt == T_OBJECT || recv_bt == T_ARRAY) + decode_flags_result |= _dmf_has_receiver; + } else { + // non-static method + decode_flags_result |= _dmf_has_receiver; + if (!m->can_be_statically_bound() && !m->is_initializer()) { + decode_flags_result |= _dmf_does_dispatch; + if (Klass::cast(m->method_holder())->is_interface()) + decode_flags_result |= _dmf_from_interface; + } + } + return m; +} + + +// A trusted party is handing us a cookie to determine a method. +// Let's boil it down to the method oop they really want. +methodOop MethodHandles::decode_method(oop x, klassOop& receiver_limit_result, int& decode_flags_result) { + decode_flags_result = 0; + receiver_limit_result = NULL; + klassOop xk = x->klass(); + if (xk == Universe::methodKlassObj()) { + return decode_methodOop((methodOop) x, decode_flags_result); + } else if (xk == SystemDictionary::MemberName_klass()) { + // Note: This only works if the MemberName has already been resolved. + return decode_MemberName(x, receiver_limit_result, decode_flags_result); + } else if (java_dyn_MethodHandle::is_subclass(xk)) { + return decode_MethodHandle(x, receiver_limit_result, decode_flags_result); + } else if (xk == SystemDictionary::reflect_method_klass()) { + oop clazz = java_lang_reflect_Method::clazz(x); + int slot = java_lang_reflect_Method::slot(x); + klassOop k = java_lang_Class::as_klassOop(clazz); + if (k != NULL && Klass::cast(k)->oop_is_instance()) + return decode_methodOop(instanceKlass::cast(k)->method_with_idnum(slot), + decode_flags_result); + } else if (xk == SystemDictionary::reflect_constructor_klass()) { + oop clazz = java_lang_reflect_Constructor::clazz(x); + int slot = java_lang_reflect_Constructor::slot(x); + klassOop k = java_lang_Class::as_klassOop(clazz); + if (k != NULL && Klass::cast(k)->oop_is_instance()) + return decode_methodOop(instanceKlass::cast(k)->method_with_idnum(slot), + decode_flags_result); + } else { + // unrecognized object + assert(!x->is_method(), "already checked"); + assert(!sun_dyn_MemberName::is_instance(x), "already checked"); + } + return NULL; +} + + +int MethodHandles::decode_MethodHandle_stack_pushes(oop mh) { + if (mh->klass() == SystemDictionary::DirectMethodHandle_klass()) + return 0; // no push/pop + int this_vmslots = java_dyn_MethodHandle::vmslots(mh); + int last_vmslots = 0; + oop last_mh = mh; + for (;;) { + oop target = java_dyn_MethodHandle::vmtarget(last_mh); + if (target->klass() == SystemDictionary::DirectMethodHandle_klass()) { + last_vmslots = java_dyn_MethodHandle::vmslots(target); + break; + } else if (!java_dyn_MethodHandle::is_instance(target)) { + // might be klass or method + assert(target->is_method(), "must get here with a direct ref to method"); + last_vmslots = methodOop(target)->size_of_parameters(); + break; + } + last_mh = target; + } + // If I am called with fewer VM slots than my ultimate callee, + // it must be that I push the additionally needed slots. + // Likewise if am called with more VM slots, I will pop them. + return (last_vmslots - this_vmslots); +} + + +// MemberName support + +// import sun_dyn_MemberName.* +enum { + IS_METHOD = sun_dyn_MemberName::MN_IS_METHOD, + IS_CONSTRUCTOR = sun_dyn_MemberName::MN_IS_CONSTRUCTOR, + IS_FIELD = sun_dyn_MemberName::MN_IS_FIELD, + IS_TYPE = sun_dyn_MemberName::MN_IS_TYPE, + SEARCH_SUPERCLASSES = sun_dyn_MemberName::MN_SEARCH_SUPERCLASSES, + SEARCH_INTERFACES = sun_dyn_MemberName::MN_SEARCH_INTERFACES, + ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE, + VM_INDEX_UNINITIALIZED = sun_dyn_MemberName::VM_INDEX_UNINITIALIZED +}; + +void MethodHandles::init_MemberName(oop mname_oop, oop target_oop) { + if (target_oop->klass() == SystemDictionary::reflect_field_klass()) { + oop clazz = java_lang_reflect_Field::clazz(target_oop); // fd.field_holder() + int slot = java_lang_reflect_Field::slot(target_oop); // fd.index() + int mods = java_lang_reflect_Field::modifiers(target_oop); + klassOop k = java_lang_Class::as_klassOop(clazz); + int offset = instanceKlass::cast(k)->offset_from_fields(slot); + init_MemberName(mname_oop, k, accessFlags_from(mods), offset); + } else { + int decode_flags = 0; klassOop receiver_limit = NULL; + methodOop m = MethodHandles::decode_method(target_oop, + receiver_limit, decode_flags); + bool do_dispatch = ((decode_flags & MethodHandles::_dmf_does_dispatch) != 0); + init_MemberName(mname_oop, m, do_dispatch); + } +} + +void MethodHandles::init_MemberName(oop mname_oop, methodOop m, bool do_dispatch) { + int flags = ((m->is_initializer() ? IS_CONSTRUCTOR : IS_METHOD) + | (jushort)( m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS )); + oop vmtarget = m; + int vmindex = methodOopDesc::invalid_vtable_index; // implies no info yet + if (!do_dispatch || (flags & IS_CONSTRUCTOR) || m->can_be_statically_bound()) + vmindex = methodOopDesc::nonvirtual_vtable_index; // implies never any dispatch + assert(vmindex != VM_INDEX_UNINITIALIZED, "Java sentinel value"); + sun_dyn_MemberName::set_vmtarget(mname_oop, vmtarget); + sun_dyn_MemberName::set_vmindex(mname_oop, vmindex); + sun_dyn_MemberName::set_flags(mname_oop, flags); +} + +void MethodHandles::init_MemberName(oop mname_oop, klassOop field_holder, AccessFlags mods, int offset) { + int flags = (IS_FIELD | (jushort)( mods.as_short() & JVM_RECOGNIZED_FIELD_MODIFIERS )); + oop vmtarget = field_holder; + int vmindex = offset; // implies no info yet + assert(vmindex != VM_INDEX_UNINITIALIZED, "bad alias on vmindex"); + sun_dyn_MemberName::set_vmtarget(mname_oop, vmtarget); + sun_dyn_MemberName::set_vmindex(mname_oop, vmindex); + sun_dyn_MemberName::set_flags(mname_oop, flags); +} + + +methodOop MethodHandles::decode_MemberName(oop mname, klassOop& receiver_limit_result, int& decode_flags_result) { + int flags = sun_dyn_MemberName::flags(mname); + if ((flags & (IS_METHOD | IS_CONSTRUCTOR)) == 0) return NULL; // not invocable + oop vmtarget = sun_dyn_MemberName::vmtarget(mname); + int vmindex = sun_dyn_MemberName::vmindex(mname); + if (vmindex == VM_INDEX_UNINITIALIZED) return NULL; // not resolved + return decode_vmtarget(vmtarget, vmindex, NULL, receiver_limit_result, decode_flags_result); +} + +// An unresolved member name is a mere symbolic reference. +// Resolving it plants a vmtarget/vmindex in it, +// which refers dirctly to JVM internals. +void MethodHandles::resolve_MemberName(Handle mname, TRAPS) { + assert(sun_dyn_MemberName::is_instance(mname()), ""); +#ifdef ASSERT + // If this assert throws, renegotiate the sentinel value used by the Java code, + // so that it is distinct from any valid vtable index value, and any special + // values defined in methodOopDesc::VtableIndexFlag. + // The point of the slop is to give the Java code and the JVM some room + // to independently specify sentinel values. + const int sentinel_slop = 10; + const int sentinel_limit = methodOopDesc::highest_unused_vtable_index_value - sentinel_slop; + assert(VM_INDEX_UNINITIALIZED < sentinel_limit, "Java sentinel != JVM sentinels"); +#endif + if (sun_dyn_MemberName::vmindex(mname()) != VM_INDEX_UNINITIALIZED) + return; // already resolved + oop defc_oop = sun_dyn_MemberName::clazz(mname()); + oop name_str = sun_dyn_MemberName::name(mname()); + oop type_str = sun_dyn_MemberName::type(mname()); + int flags = sun_dyn_MemberName::flags(mname()); + + if (defc_oop == NULL || name_str == NULL || type_str == NULL) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "nothing to resolve"); + } + klassOop defc_klassOop = java_lang_Class::as_klassOop(defc_oop); + defc_oop = NULL; // safety + if (defc_klassOop == NULL) return; // a primitive; no resolution possible + if (!Klass::cast(defc_klassOop)->oop_is_instance()) { + if (!Klass::cast(defc_klassOop)->oop_is_array()) return; + defc_klassOop = SystemDictionary::object_klass(); + } + instanceKlassHandle defc(THREAD, defc_klassOop); + defc_klassOop = NULL; // safety + if (defc.is_null()) { + THROW_MSG(vmSymbols::java_lang_InternalError(), "primitive class"); + } + defc->link_class(CHECK); + + // convert the external string name to an internal symbol + symbolHandle name(THREAD, java_lang_String::as_symbol_or_null(name_str)); + if (name.is_null()) return; // no such name + name_str = NULL; // safety + + // convert the external string or reflective type to an internal signature + bool force_signature = (name() == vmSymbols::invoke_name()); + symbolHandle type; { + symbolOop type_sym = NULL; + if (java_dyn_MethodType::is_instance(type_str)) { + type_sym = java_dyn_MethodType::as_signature(type_str, force_signature, CHECK); + } else if (java_lang_Class::is_instance(type_str)) { + type_sym = java_lang_Class::as_signature(type_str, force_signature, CHECK); + } else if (java_lang_String::is_instance(type_str)) { + if (force_signature) { + type = java_lang_String::as_symbol(type_str, CHECK); + } else { + type_sym = java_lang_String::as_symbol_or_null(type_str); + } + } else { + THROW_MSG(vmSymbols::java_lang_InternalError(), "unrecognized type"); + } + if (type_sym != NULL) + type = symbolHandle(THREAD, type_sym); + } + if (type.is_null()) return; // no such signature exists in the VM + type_str = NULL; // safety + + // Time to do the lookup. + switch (flags & ALL_KINDS) { + case IS_METHOD: + { + CallInfo result; + { + EXCEPTION_MARK; + if ((flags & JVM_ACC_STATIC) != 0) { + LinkResolver::resolve_static_call(result, + defc, name, type, KlassHandle(), false, false, THREAD); + } else if (defc->is_interface()) { + LinkResolver::resolve_interface_call(result, Handle(), defc, + defc, name, type, KlassHandle(), false, false, THREAD); + } else { + LinkResolver::resolve_virtual_call(result, Handle(), defc, + defc, name, type, KlassHandle(), false, false, THREAD); + } + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return; + } + } + methodHandle m = result.resolved_method(); + oop vmtarget = NULL; + int vmindex = methodOopDesc::nonvirtual_vtable_index; + if (defc->is_interface()) { + vmindex = klassItable::compute_itable_index(m()); + assert(vmindex >= 0, ""); + } else if (result.has_vtable_index()) { + vmindex = result.vtable_index(); + assert(vmindex >= 0, ""); + } + assert(vmindex != VM_INDEX_UNINITIALIZED, ""); + if (vmindex < 0) { + assert(result.is_statically_bound(), ""); + vmtarget = m(); + } else { + vmtarget = result.resolved_klass()->as_klassOop(); + } + int mods = (m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS); + sun_dyn_MemberName::set_vmtarget(mname(), vmtarget); + sun_dyn_MemberName::set_vmindex(mname(), vmindex); + sun_dyn_MemberName::set_modifiers(mname(), mods); + DEBUG_ONLY(int junk; klassOop junk2); + assert(decode_MemberName(mname(), junk2, junk) == result.resolved_method()(), + "properly stored for later decoding"); + return; + } + case IS_CONSTRUCTOR: + { + CallInfo result; + { + EXCEPTION_MARK; + if (name() == vmSymbols::object_initializer_name()) { + LinkResolver::resolve_special_call(result, + defc, name, type, KlassHandle(), false, THREAD); + } else { + break; // will throw after end of switch + } + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return; + } + } + assert(result.is_statically_bound(), ""); + methodHandle m = result.resolved_method(); + oop vmtarget = m(); + int vmindex = methodOopDesc::nonvirtual_vtable_index; + int mods = (m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS); + sun_dyn_MemberName::set_vmtarget(mname(), vmtarget); + sun_dyn_MemberName::set_vmindex(mname(), vmindex); + sun_dyn_MemberName::set_modifiers(mname(), mods); + DEBUG_ONLY(int junk; klassOop junk2); + assert(decode_MemberName(mname(), junk2, junk) == result.resolved_method()(), + "properly stored for later decoding"); + return; + } + case IS_FIELD: + { + // This is taken from LinkResolver::resolve_field, sans access checks. + fieldDescriptor fd; // find_field initializes fd if found + KlassHandle sel_klass(THREAD, instanceKlass::cast(defc())->find_field(name(), type(), &fd)); + // check if field exists; i.e., if a klass containing the field def has been selected + if (sel_klass.is_null()) return; + oop vmtarget = sel_klass->as_klassOop(); + int vmindex = fd.offset(); + int mods = (fd.access_flags().as_short() & JVM_RECOGNIZED_FIELD_MODIFIERS); + if (vmindex == VM_INDEX_UNINITIALIZED) break; // should not happen + sun_dyn_MemberName::set_vmtarget(mname(), vmtarget); + sun_dyn_MemberName::set_vmindex(mname(), vmindex); + sun_dyn_MemberName::set_modifiers(mname(), mods); + return; + } + } + THROW_MSG(vmSymbols::java_lang_InternalError(), "unrecognized MemberName format"); +} + +// Conversely, a member name which is only initialized from JVM internals +// may have null defc, name, and type fields. +// Resolving it plants a vmtarget/vmindex in it, +// which refers directly to JVM internals. +void MethodHandles::expand_MemberName(Handle mname, int suppress, TRAPS) { + assert(sun_dyn_MemberName::is_instance(mname()), ""); + oop vmtarget = sun_dyn_MemberName::vmtarget(mname()); + int vmindex = sun_dyn_MemberName::vmindex(mname()); + if (vmtarget == NULL || vmindex == VM_INDEX_UNINITIALIZED) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "nothing to expand"); + } + + bool have_defc = (sun_dyn_MemberName::clazz(mname()) != NULL); + bool have_name = (sun_dyn_MemberName::name(mname()) != NULL); + bool have_type = (sun_dyn_MemberName::type(mname()) != NULL); + int flags = sun_dyn_MemberName::flags(mname()); + + if (suppress != 0) { + if (suppress & _suppress_defc) have_defc = true; + if (suppress & _suppress_name) have_name = true; + if (suppress & _suppress_type) have_type = true; + } + + if (have_defc && have_name && have_type) return; // nothing needed + + switch (flags & ALL_KINDS) { + case IS_METHOD: + case IS_CONSTRUCTOR: + { + klassOop receiver_limit = NULL; + int decode_flags = 0; + methodHandle m(THREAD, decode_vmtarget(vmtarget, vmindex, NULL, + receiver_limit, decode_flags)); + if (m.is_null()) break; + if (!have_defc) { + klassOop defc = m->method_holder(); + if (receiver_limit != NULL && receiver_limit != defc + && Klass::cast(receiver_limit)->is_subtype_of(defc)) + defc = receiver_limit; + sun_dyn_MemberName::set_clazz(mname(), Klass::cast(defc)->java_mirror()); + } + if (!have_name) { + //not java_lang_String::create_from_symbol; let's intern member names + Handle name = StringTable::intern(m->name(), CHECK); + sun_dyn_MemberName::set_name(mname(), name()); + } + if (!have_type) { + Handle type = java_lang_String::create_from_symbol(m->signature(), CHECK); + sun_dyn_MemberName::set_type(mname(), type()); + } + return; + } + case IS_FIELD: + { + // This is taken from LinkResolver::resolve_field, sans access checks. + if (!vmtarget->is_klass()) break; + if (!Klass::cast((klassOop) vmtarget)->oop_is_instance()) break; + instanceKlassHandle defc(THREAD, (klassOop) vmtarget); + bool is_static = ((flags & JVM_ACC_STATIC) != 0); + fieldDescriptor fd; // find_field initializes fd if found + if (!defc->find_field_from_offset(vmindex, is_static, &fd)) + break; // cannot expand + if (!have_defc) { + sun_dyn_MemberName::set_clazz(mname(), defc->java_mirror()); + } + if (!have_name) { + //not java_lang_String::create_from_symbol; let's intern member names + Handle name = StringTable::intern(fd.name(), CHECK); + sun_dyn_MemberName::set_name(mname(), name()); + } + if (!have_type) { + Handle type = java_lang_String::create_from_symbol(fd.signature(), CHECK); + sun_dyn_MemberName::set_type(mname(), type()); + } + return; + } + } + THROW_MSG(vmSymbols::java_lang_InternalError(), "unrecognized MemberName format"); +} + +int MethodHandles::find_MemberNames(klassOop k, + symbolOop name, symbolOop sig, + int mflags, klassOop caller, + int skip, objArrayOop results) { + DEBUG_ONLY(No_Safepoint_Verifier nsv); + // this code contains no safepoints! + + // %%% take caller into account! + + if (k == NULL || !Klass::cast(k)->oop_is_instance()) return -1; + + int rfill = 0, rlimit = results->length(), rskip = skip; + // overflow measurement: + int overflow = 0, overflow_limit = MAX2(1000, rlimit); + + int match_flags = mflags; + bool search_superc = ((match_flags & SEARCH_SUPERCLASSES) != 0); + bool search_intfc = ((match_flags & SEARCH_INTERFACES) != 0); + bool local_only = !(search_superc | search_intfc); + bool classes_only = false; + + if (name != NULL) { + if (name->utf8_length() == 0) return 0; // a match is not possible + } + if (sig != NULL) { + if (sig->utf8_length() == 0) return 0; // a match is not possible + if (sig->byte_at(0) == '(') + match_flags &= ~(IS_FIELD | IS_TYPE); + else + match_flags &= ~(IS_CONSTRUCTOR | IS_METHOD); + } + + if ((match_flags & IS_TYPE) != 0) { + // NYI, and Core Reflection works quite well for this query + } + + if ((match_flags & IS_FIELD) != 0) { + for (FieldStream st(k, local_only, !search_intfc); !st.eos(); st.next()) { + if (name != NULL && st.name() != name) + continue; + if (sig != NULL && st.signature() != sig) + continue; + // passed the filters + if (rskip > 0) { + --rskip; + } else if (rfill < rlimit) { + oop result = results->obj_at(rfill++); + if (!sun_dyn_MemberName::is_instance(result)) + return -99; // caller bug! + MethodHandles::init_MemberName(result, st.klass()->as_klassOop(), st.access_flags(), st.offset()); + } else if (++overflow >= overflow_limit) { + match_flags = 0; break; // got tired of looking at overflow + } + } + } + + if ((match_flags & (IS_METHOD | IS_CONSTRUCTOR)) != 0) { + // watch out for these guys: + symbolOop init_name = vmSymbols::object_initializer_name(); + symbolOop clinit_name = vmSymbols::class_initializer_name(); + if (name == clinit_name) clinit_name = NULL; // hack for exposing + bool negate_name_test = false; + // fix name so that it captures the intention of IS_CONSTRUCTOR + if (!(match_flags & IS_METHOD)) { + // constructors only + if (name == NULL) { + name = init_name; + } else if (name != init_name) { + return 0; // no constructors of this method name + } + } else if (!(match_flags & IS_CONSTRUCTOR)) { + // methods only + if (name == NULL) { + name = init_name; + negate_name_test = true; // if we see the name, we *omit* the entry + } else if (name == init_name) { + return 0; // no methods of this constructor name + } + } else { + // caller will accept either sort; no need to adjust name + } + for (MethodStream st(k, local_only, !search_intfc); !st.eos(); st.next()) { + methodOop m = st.method(); + symbolOop m_name = m->name(); + if (m_name == clinit_name) + continue; + if (name != NULL && ((m_name != name) ^ negate_name_test)) + continue; + if (sig != NULL && m->signature() != sig) + continue; + // passed the filters + if (rskip > 0) { + --rskip; + } else if (rfill < rlimit) { + oop result = results->obj_at(rfill++); + if (!sun_dyn_MemberName::is_instance(result)) + return -99; // caller bug! + MethodHandles::init_MemberName(result, m, true); + } else if (++overflow >= overflow_limit) { + match_flags = 0; break; // got tired of looking at overflow + } + } + } + + // return number of elements we at leasted wanted to initialize + return rfill + overflow; +} + + + + +// Decode the vmtarget field of a method handle. +// Sanitize out methodOops, klassOops, and any other non-Java data. +// This is for debugging and reflection. +oop MethodHandles::encode_target(Handle mh, int format, TRAPS) { + assert(java_dyn_MethodHandle::is_instance(mh()), "must be a MH"); + if (format == ETF_HANDLE_OR_METHOD_NAME) { + oop target = java_dyn_MethodHandle::vmtarget(mh()); + if (target == NULL) { + return NULL; // unformed MH + } + klassOop tklass = target->klass(); + if (Klass::cast(tklass)->is_subclass_of(SystemDictionary::object_klass())) { + return target; // target is another MH (or something else?) + } + } + if (format == ETF_DIRECT_HANDLE) { + oop target = mh(); + for (;;) { + if (target->klass() == SystemDictionary::DirectMethodHandle_klass()) { + return target; + } + if (!java_dyn_MethodHandle::is_instance(target)){ + return NULL; // unformed MH + } + target = java_dyn_MethodHandle::vmtarget(target); + } + } + // cases of metadata in MH.vmtarget: + // - AMH can have methodOop for static invoke with bound receiver + // - DMH can have methodOop for static invoke (on variable receiver) + // - DMH can have klassOop for dispatched (non-static) invoke + klassOop receiver_limit = NULL; + int decode_flags = 0; + methodOop m = decode_MethodHandle(mh(), receiver_limit, decode_flags); + if (m == NULL) return NULL; + switch (format) { + case ETF_REFLECT_METHOD: + // same as jni_ToReflectedMethod: + if (m->is_initializer()) { + return Reflection::new_constructor(m, THREAD); + } else { + return Reflection::new_method(m, UseNewReflection, false, THREAD); + } + + case ETF_HANDLE_OR_METHOD_NAME: // method, not handle + case ETF_METHOD_NAME: + { + if (SystemDictionary::MemberName_klass() == NULL) break; + instanceKlassHandle mname_klass(THREAD, SystemDictionary::MemberName_klass()); + mname_klass->initialize(CHECK_NULL); + Handle mname = mname_klass->allocate_instance_handle(CHECK_NULL); + sun_dyn_MemberName::set_vmindex(mname(), VM_INDEX_UNINITIALIZED); + bool do_dispatch = ((decode_flags & MethodHandles::_dmf_does_dispatch) != 0); + init_MemberName(mname(), m, do_dispatch); + expand_MemberName(mname, 0, CHECK_NULL); + return mname(); + } + } + + // Unknown format code. + char msg[50]; + jio_snprintf(msg, sizeof(msg), "unknown getTarget format=%d", format); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), msg); +} + +bool MethodHandles::class_cast_needed(klassOop src, klassOop dst) { + if (src == dst || dst == SystemDictionary::object_klass()) + return false; // quickest checks + Klass* srck = Klass::cast(src); + Klass* dstk = Klass::cast(dst); + if (dstk->is_interface()) { + // interface receivers can safely be viewed as untyped, + // because interface calls always include a dynamic check + //dstk = Klass::cast(SystemDictionary::object_klass()); + return false; + } + if (srck->is_interface()) { + // interface arguments must be viewed as untyped + //srck = Klass::cast(SystemDictionary::object_klass()); + return true; + } + return !srck->is_subclass_of(dstk->as_klassOop()); +} + +static oop object_java_mirror() { + return Klass::cast(SystemDictionary::object_klass())->java_mirror(); +} + +bool MethodHandles::same_basic_type_for_arguments(BasicType src, + BasicType dst, + bool for_return) { + // return values can always be forgotten: + if (for_return && dst == T_VOID) return true; + assert(src != T_VOID && dst != T_VOID, "should not be here"); + if (src == dst) return true; + if (type2size[src] != type2size[dst]) return false; + // allow reinterpretation casts for integral widening + if (is_subword_type(src)) { // subwords can fit in int or other subwords + if (dst == T_INT) // any subword fits in an int + return true; + if (src == T_BOOLEAN) // boolean fits in any subword + return is_subword_type(dst); + if (src == T_BYTE && dst == T_SHORT) + return true; // remaining case: byte fits in short + } + // allow float/fixed reinterpretation casts + if (src == T_FLOAT) return dst == T_INT; + if (src == T_INT) return dst == T_FLOAT; + if (src == T_DOUBLE) return dst == T_LONG; + if (src == T_LONG) return dst == T_DOUBLE; + return false; +} + +const char* MethodHandles::check_method_receiver(methodOop m, + klassOop passed_recv_type) { + assert(!m->is_static(), "caller resp."); + if (passed_recv_type == NULL) + return "receiver type is primitive"; + if (class_cast_needed(passed_recv_type, m->method_holder())) { + Klass* formal = Klass::cast(m->method_holder()); + return SharedRuntime::generate_class_cast_message("receiver type", + formal->external_name()); + } + return NULL; // checks passed +} + +// Verify that m's signature can be called type-safely by a method handle +// of the given method type 'mtype'. +// It takes a TRAPS argument because it must perform symbol lookups. +void MethodHandles::verify_method_signature(methodHandle m, + Handle mtype, + int first_ptype_pos, + KlassHandle insert_ptype, + TRAPS) { + objArrayHandle ptypes(THREAD, java_dyn_MethodType::ptypes(mtype())); + int pnum = first_ptype_pos; + int pmax = ptypes->length(); + int mnum = 0; // method argument + const char* err = NULL; + for (SignatureStream ss(m->signature()); !ss.is_done(); ss.next()) { + oop ptype_oop = NULL; + if (ss.at_return_type()) { + if (pnum != pmax) + { err = "too many arguments"; break; } + ptype_oop = java_dyn_MethodType::rtype(mtype()); + } else { + if (pnum >= pmax) + { err = "not enough arguments"; break; } + if (pnum >= 0) + ptype_oop = ptypes->obj_at(pnum); + else if (insert_ptype.is_null()) + ptype_oop = NULL; + else + ptype_oop = insert_ptype->java_mirror(); + pnum += 1; + mnum += 1; + } + klassOop mklass = NULL; + BasicType mtype = ss.type(); + if (mtype == T_ARRAY) mtype = T_OBJECT; // fold all refs to T_OBJECT + if (mtype == T_OBJECT) { + if (ptype_oop == NULL) { + // null matches any reference + continue; + } + // If we fail to resolve types at this point, we will throw an error. + symbolOop name_oop = ss.as_symbol(CHECK); + symbolHandle name(THREAD, name_oop); + instanceKlass* mk = instanceKlass::cast(m->method_holder()); + Handle loader(THREAD, mk->class_loader()); + Handle domain(THREAD, mk->protection_domain()); + mklass = SystemDictionary::resolve_or_fail(name, loader, domain, + true, CHECK); + } + if (ptype_oop == NULL) { + // null does not match any non-reference; use Object to report the error + ptype_oop = object_java_mirror(); + } + klassOop pklass = NULL; + BasicType ptype = java_lang_Class::as_BasicType(ptype_oop, &pklass); + if (!ss.at_return_type()) { + err = check_argument_type_change(ptype, pklass, mtype, mklass, mnum); + } else { + err = check_return_type_change(mtype, mklass, ptype, pklass); // note reversal! + } + if (err != NULL) break; + } + + if (err != NULL) { + THROW_MSG(vmSymbols::java_lang_InternalError(), err); + } +} + +// Main routine for verifying the MethodHandle.type of a proposed +// direct or bound-direct method handle. +void MethodHandles::verify_method_type(methodHandle m, + Handle mtype, + bool has_bound_recv, + KlassHandle bound_recv_type, + TRAPS) { + bool m_needs_receiver = !m->is_static(); + + const char* err = NULL; + + int first_ptype_pos = m_needs_receiver ? 1 : 0; + if (has_bound_recv && err == NULL) { + first_ptype_pos -= 1; + if (m_needs_receiver && bound_recv_type.is_null()) + { err = "bound receiver is not an object"; goto die; } + } + + if (m_needs_receiver && err == NULL) { + objArrayOop ptypes = java_dyn_MethodType::ptypes(mtype()); + if (ptypes->length() < first_ptype_pos) + { err = "receiver argument is missing"; goto die; } + if (first_ptype_pos == -1) + err = check_method_receiver(m(), bound_recv_type->as_klassOop()); + else + err = check_method_receiver(m(), java_lang_Class::as_klassOop(ptypes->obj_at(0))); + if (err != NULL) goto die; + } + + // Check the other arguments for mistypes. + verify_method_signature(m, mtype, first_ptype_pos, bound_recv_type, CHECK); + return; + + die: + THROW_MSG(vmSymbols::java_lang_InternalError(), err); +} + +void MethodHandles::verify_vmslots(Handle mh, TRAPS) { + // Verify vmslots. + int check_slots = argument_slot_count(java_dyn_MethodHandle::type(mh())); + if (java_dyn_MethodHandle::vmslots(mh()) != check_slots) { + THROW_MSG(vmSymbols::java_lang_InternalError(), "bad vmslots in BMH"); + } +} + +void MethodHandles::verify_vmargslot(Handle mh, int argnum, int argslot, TRAPS) { + // Verify that argslot points at the given argnum. + int check_slot = argument_slot(java_dyn_MethodHandle::type(mh()), argnum); + if (argslot != check_slot || argslot < 0) { + const char* fmt = "for argnum of %d, vmargslot is %d, should be %d"; + size_t msglen = strlen(fmt) + 3*11 + 1; + char* msg = NEW_RESOURCE_ARRAY(char, msglen); + jio_snprintf(msg, msglen, fmt, argnum, argslot, check_slot); + THROW_MSG(vmSymbols::java_lang_InternalError(), msg); + } +} + +// Verify the correspondence between two method types. +// Apart from the advertised changes, caller method type X must +// be able to invoke the callee method Y type with no violations +// of type integrity. +// Return NULL if all is well, else a short error message. +const char* MethodHandles::check_method_type_change(oop src_mtype, int src_beg, int src_end, + int insert_argnum, oop insert_type, + int change_argnum, oop change_type, + int delete_argnum, + oop dst_mtype, int dst_beg, int dst_end) { + objArrayOop src_ptypes = java_dyn_MethodType::ptypes(src_mtype); + objArrayOop dst_ptypes = java_dyn_MethodType::ptypes(dst_mtype); + + int src_max = src_ptypes->length(); + int dst_max = dst_ptypes->length(); + + if (src_end == -1) src_end = src_max; + if (dst_end == -1) dst_end = dst_max; + + assert(0 <= src_beg && src_beg <= src_end && src_end <= src_max, "oob"); + assert(0 <= dst_beg && dst_beg <= dst_end && dst_end <= dst_max, "oob"); + + // pending actions; set to -1 when done: + int ins_idx = insert_argnum, chg_idx = change_argnum, del_idx = delete_argnum; + + const char* err = NULL; + + // Walk along each array of parameter types, including a virtual + // NULL end marker at the end of each. + for (int src_idx = src_beg, dst_idx = dst_beg; + (src_idx <= src_end && dst_idx <= dst_end); + src_idx++, dst_idx++) { + oop src_type = (src_idx == src_end) ? oop(NULL) : src_ptypes->obj_at(src_idx); + oop dst_type = (dst_idx == dst_end) ? oop(NULL) : dst_ptypes->obj_at(dst_idx); + bool fix_null_src_type = false; + + // Perform requested edits. + if (ins_idx == src_idx) { + // note that the inserted guy is never affected by a change or deletion + ins_idx = -1; + src_type = insert_type; + fix_null_src_type = true; + --src_idx; // back up to process src type on next loop + src_idx = src_end; + } else { + // note that the changed guy can be immediately deleted + if (chg_idx == src_idx) { + chg_idx = -1; + assert(src_idx < src_end, "oob"); + src_type = change_type; + fix_null_src_type = true; + } + if (del_idx == src_idx) { + del_idx = -1; + assert(src_idx < src_end, "oob"); + --dst_idx; + continue; // rerun loop after skipping this position + } + } + + if (src_type == NULL && fix_null_src_type) + // explicit null in this case matches any dest reference + src_type = (java_lang_Class::is_primitive(dst_type) ? object_java_mirror() : dst_type); + + // Compare the two argument types. + if (src_type != dst_type) { + if (src_type == NULL) return "not enough arguments"; + if (dst_type == NULL) return "too many arguments"; + err = check_argument_type_change(src_type, dst_type, dst_idx); + if (err != NULL) return err; + } + } + + // Now compare return types also. + oop src_rtype = java_dyn_MethodType::rtype(src_mtype); + oop dst_rtype = java_dyn_MethodType::rtype(dst_mtype); + if (src_rtype != dst_rtype) { + err = check_return_type_change(dst_rtype, src_rtype); // note reversal! + if (err != NULL) return err; + } + + assert(err == NULL, ""); + return NULL; // all is well +} + + +const char* MethodHandles::check_argument_type_change(BasicType src_type, + klassOop src_klass, + BasicType dst_type, + klassOop dst_klass, + int argnum) { + const char* err = NULL; + + // just in case: + if (src_type == T_ARRAY) src_type = T_OBJECT; + if (dst_type == T_ARRAY) dst_type = T_OBJECT; + + // Produce some nice messages if VerifyMethodHandles is turned on: + if (!same_basic_type_for_arguments(src_type, dst_type, (argnum < 0))) { + if (src_type == T_OBJECT) { + err = ((argnum >= 0) + ? "type mismatch: passing a %s for method argument #%d, which expects primitive %s" + : "type mismatch: returning a %s, but caller expects primitive %s"); + } else if (dst_type == T_OBJECT) { + err = ((argnum < 0) + ? "type mismatch: passing a primitive %s for method argument #%d, which expects %s" + : "type mismatch: returning a primitive %s, but caller expects %s"); + } else { + err = ((argnum < 0) + ? "type mismatch: passing a %s for method argument #%d, which expects %s" + : "type mismatch: returning a %s, but caller expects %s"); + } + } else if (src_type == T_OBJECT && class_cast_needed(src_klass, dst_klass)) { + if (!class_cast_needed(dst_klass, src_klass)) { + err = ((argnum < 0) + ? "cast required: passing a %s for method argument #%d, which expects %s" + : "cast required: returning a %s, but caller expects %s"); + } else { + err = ((argnum < 0) + ? "reference mismatch: passing a %s for method argument #%d, which expects %s" + : "reference mismatch: returning a %s, but caller expects %s"); + } + } else { + // passed the obstacle course + return NULL; + } + + // format, format, format + const char* src_name = type2name(src_type); + const char* dst_name = type2name(dst_type); + if (src_type == T_OBJECT) src_name = Klass::cast(src_klass)->external_name(); + if (dst_type == T_OBJECT) dst_name = Klass::cast(dst_klass)->external_name(); + if (src_name == NULL) src_name = "unknown type"; + if (dst_name == NULL) dst_name = "unknown type"; + + size_t msglen = strlen(err) + strlen(src_name) + strlen(dst_name) + (argnum < 10 ? 1 : 11); + char* msg = NEW_RESOURCE_ARRAY(char, msglen + 1); + if (argnum >= 0) { + assert(strstr(err, "%d") != NULL, ""); + jio_snprintf(msg, msglen, err, src_name, argnum, dst_name); + } else { + assert(strstr(err, "%d") == NULL, ""); + jio_snprintf(msg, msglen, err, src_name, dst_name); + } + return msg; +} + +// Compute the depth within the stack of the given argument, i.e., +// the combined size of arguments to the right of the given argument. +// For the last argument (ptypes.length-1) this will be zero. +// For the first argument (0) this will be the size of all +// arguments but that one. For the special number -1, this +// will be the size of all arguments, including the first. +// If the argument is neither -1 nor a valid argument index, +// then return a negative number. Otherwise, the result +// is in the range [0..vmslots] inclusive. +int MethodHandles::argument_slot(oop method_type, int arg) { + objArrayOop ptypes = java_dyn_MethodType::ptypes(method_type); + int argslot = 0; + int len = ptypes->length(); + if (arg < -1 || arg >= len) return -99; + for (int i = len-1; i > arg; i--) { + BasicType bt = java_lang_Class::as_BasicType(ptypes->obj_at(i)); + argslot += type2size[bt]; + } + assert(argument_slot_to_argnum(method_type, argslot) == arg, "inverse works"); + return argslot; +} + +// Given a slot number, return the argument number. +int MethodHandles::argument_slot_to_argnum(oop method_type, int query_argslot) { + objArrayOop ptypes = java_dyn_MethodType::ptypes(method_type); + int argslot = 0; + int len = ptypes->length(); + for (int i = len-1; i >= 0; i--) { + if (query_argslot == argslot) return i; + BasicType bt = java_lang_Class::as_BasicType(ptypes->obj_at(i)); + argslot += type2size[bt]; + } + // return pseudo-arg deepest in stack: + if (query_argslot == argslot) return -1; + return -99; // oob slot, or splitting a double-slot arg +} + +methodHandle MethodHandles::dispatch_decoded_method(methodHandle m, + KlassHandle receiver_limit, + int decode_flags, + KlassHandle receiver_klass, + TRAPS) { + assert((decode_flags & ~_DMF_DIRECT_MASK) == 0, "must be direct method reference"); + assert((decode_flags & _dmf_has_receiver) != 0, "must have a receiver or first reference argument"); + + if (!m->is_static() && + (receiver_klass.is_null() || !receiver_klass->is_subtype_of(m->method_holder()))) + // given type does not match class of method, or receiver is null! + // caller should have checked this, but let's be extra careful... + return methodHandle(); + + if (receiver_limit.not_null() && + (receiver_klass.not_null() && !receiver_klass->is_subtype_of(receiver_limit()))) + // given type is not limited to the receiver type + // note that a null receiver can match any reference value, for a static method + return methodHandle(); + + if (!(decode_flags & MethodHandles::_dmf_does_dispatch)) { + // pre-dispatched or static method (null receiver is OK for static) + return m; + + } else if (receiver_klass.is_null()) { + // null receiver value; cannot dispatch + return methodHandle(); + + } else if (!(decode_flags & MethodHandles::_dmf_from_interface)) { + // perform virtual dispatch + int vtable_index = m->vtable_index(); + guarantee(vtable_index >= 0, "valid vtable index"); + + // receiver_klass might be an arrayKlassOop but all vtables start at + // the same place. The cast is to avoid virtual call and assertion. + // See also LinkResolver::runtime_resolve_virtual_method. + instanceKlass* inst = (instanceKlass*)Klass::cast(receiver_klass()); + DEBUG_ONLY(inst->verify_vtable_index(vtable_index)); + methodOop m_oop = inst->method_at_vtable(vtable_index); + return methodHandle(THREAD, m_oop); + + } else { + // perform interface dispatch + int itable_index = klassItable::compute_itable_index(m()); + guarantee(itable_index >= 0, "valid itable index"); + instanceKlass* inst = instanceKlass::cast(receiver_klass()); + methodOop m_oop = inst->method_at_itable(m->method_holder(), itable_index, THREAD); + return methodHandle(THREAD, m_oop); + } +} + +void MethodHandles::verify_DirectMethodHandle(Handle mh, methodHandle m, TRAPS) { + // Verify type. + Handle mtype(THREAD, java_dyn_MethodHandle::type(mh())); + verify_method_type(m, mtype, false, KlassHandle(), CHECK); + + // Verify vmslots. + if (java_dyn_MethodHandle::vmslots(mh()) != m->size_of_parameters()) { + THROW_MSG(vmSymbols::java_lang_InternalError(), "bad vmslots in DMH"); + } +} + +void MethodHandles::init_DirectMethodHandle(Handle mh, methodHandle m, bool do_dispatch, TRAPS) { + // Check arguments. + if (mh.is_null() || m.is_null() || + (!do_dispatch && m->is_abstract())) { + THROW(vmSymbols::java_lang_InternalError()); + } + + java_dyn_MethodHandle::init_vmslots(mh()); + + if (VerifyMethodHandles) { + // The privileged code which invokes this routine should not make + // a mistake about types, but it's better to verify. + verify_DirectMethodHandle(mh, m, CHECK); + } + + // Finally, after safety checks are done, link to the target method. + // We will follow the same path as the latter part of + // InterpreterRuntime::resolve_invoke(), which first finds the method + // and then decides how to populate the constant pool cache entry + // that links the interpreter calls to the method. We need the same + // bits, and will use the same calling sequence code. + + int vmindex = methodOopDesc::garbage_vtable_index; + oop vmtarget = NULL; + + instanceKlass::cast(m->method_holder())->link_class(CHECK); + + MethodHandleEntry* me = NULL; + if (do_dispatch && Klass::cast(m->method_holder())->is_interface()) { + // We are simulating an invokeinterface instruction. + // (We might also be simulating an invokevirtual on a miranda method, + // but it is safe to treat it as an invokeinterface.) + assert(!m->can_be_statically_bound(), "no final methods on interfaces"); + vmindex = klassItable::compute_itable_index(m()); + assert(vmindex >= 0, "(>=0) == do_dispatch"); + // Set up same bits as ConstantPoolCacheEntry::set_interface_call(). + vmtarget = m->method_holder(); // the interface + me = MethodHandles::entry(MethodHandles::_invokeinterface_mh); + } else if (!do_dispatch || m->can_be_statically_bound()) { + // We are simulating an invokestatic or invokespecial instruction. + // Set up the method pointer, just like ConstantPoolCacheEntry::set_method(). + vmtarget = m(); + // this does not help dispatch, but it will make it possible to parse this MH: + vmindex = methodOopDesc::nonvirtual_vtable_index; + assert(vmindex < 0, "(>=0) == do_dispatch"); + if (!m->is_static()) { + me = MethodHandles::entry(MethodHandles::_invokespecial_mh); + } else { + me = MethodHandles::entry(MethodHandles::_invokestatic_mh); + // Part of the semantics of a static call is an initialization barrier. + // For a DMH, it is done now, when the handle is created. + Klass* k = Klass::cast(m->method_holder()); + if (k->should_be_initialized()) { + k->initialize(CHECK); + } + } + } else { + // We are simulating an invokevirtual instruction. + // Set up the vtable index, just like ConstantPoolCacheEntry::set_method(). + // The key logic is LinkResolver::runtime_resolve_virtual_method. + vmindex = m->vtable_index(); + vmtarget = m->method_holder(); + me = MethodHandles::entry(MethodHandles::_invokevirtual_mh); + } + + if (me == NULL) { THROW(vmSymbols::java_lang_InternalError()); } + + sun_dyn_DirectMethodHandle::set_vmtarget(mh(), vmtarget); + sun_dyn_DirectMethodHandle::set_vmindex(mh(), vmindex); + DEBUG_ONLY(int flags; klassOop rlimit); + assert(MethodHandles::decode_method(mh(), rlimit, flags) == m(), + "properly stored for later decoding"); + DEBUG_ONLY(bool actual_do_dispatch = ((flags & _dmf_does_dispatch) != 0)); + assert(!(actual_do_dispatch && !do_dispatch), + "do not perform dispatch if !do_dispatch specified"); + assert(actual_do_dispatch == (vmindex >= 0), "proper later decoding of do_dispatch"); + assert(decode_MethodHandle_stack_pushes(mh()) == 0, "DMH does not move stack"); + + // Done! + java_dyn_MethodHandle::set_vmentry(mh(), me); +} + +void MethodHandles::verify_BoundMethodHandle_with_receiver(Handle mh, + methodHandle m, + TRAPS) { + // Verify type. + oop receiver = sun_dyn_BoundMethodHandle::argument(mh()); + Handle mtype(THREAD, java_dyn_MethodHandle::type(mh())); + KlassHandle bound_recv_type; + if (receiver != NULL) bound_recv_type = KlassHandle(THREAD, receiver->klass()); + verify_method_type(m, mtype, true, bound_recv_type, CHECK); + + int receiver_pos = m->size_of_parameters() - 1; + + // Verify MH.vmargslot, which should point at the bound receiver. + verify_vmargslot(mh, -1, sun_dyn_BoundMethodHandle::vmargslot(mh()), CHECK); + //verify_vmslots(mh, CHECK); + + // Verify vmslots. + if (java_dyn_MethodHandle::vmslots(mh()) != receiver_pos) { + THROW_MSG(vmSymbols::java_lang_InternalError(), "bad vmslots in BMH (receiver)"); + } +} + +// Initialize a BMH with a receiver bound directly to a methodOop. +void MethodHandles::init_BoundMethodHandle_with_receiver(Handle mh, + methodHandle original_m, + KlassHandle receiver_limit, + int decode_flags, + TRAPS) { + // Check arguments. + if (mh.is_null() || original_m.is_null()) { + THROW(vmSymbols::java_lang_InternalError()); + } + + KlassHandle receiver_klass; + { + oop receiver_oop = sun_dyn_BoundMethodHandle::argument(mh()); + if (receiver_oop != NULL) + receiver_klass = KlassHandle(THREAD, receiver_oop->klass()); + } + methodHandle m = dispatch_decoded_method(original_m, + receiver_limit, decode_flags, + receiver_klass, + CHECK); + if (m.is_null()) { THROW(vmSymbols::java_lang_InternalError()); } + if (m->is_abstract()) { THROW(vmSymbols::java_lang_AbstractMethodError()); } + + java_dyn_MethodHandle::init_vmslots(mh()); + + if (VerifyMethodHandles) { + verify_BoundMethodHandle_with_receiver(mh, m, CHECK); + } + + sun_dyn_BoundMethodHandle::set_vmtarget(mh(), m()); + + DEBUG_ONLY(int junk; klassOop junk2); + assert(MethodHandles::decode_method(mh(), junk2, junk) == m(), "properly stored for later decoding"); + assert(decode_MethodHandle_stack_pushes(mh()) == 1, "BMH pushes one stack slot"); + + // Done! + java_dyn_MethodHandle::set_vmentry(mh(), MethodHandles::entry(MethodHandles::_bound_ref_direct_mh)); +} + +void MethodHandles::verify_BoundMethodHandle(Handle mh, Handle target, int argnum, + bool direct_to_method, TRAPS) { + Handle ptype_handle(THREAD, + java_dyn_MethodType::ptype(java_dyn_MethodHandle::type(target()), argnum)); + KlassHandle ptype_klass; + BasicType ptype = java_lang_Class::as_BasicType(ptype_handle(), &ptype_klass); + int slots_pushed = type2size[ptype]; + + oop argument = sun_dyn_BoundMethodHandle::argument(mh()); + + const char* err = NULL; + + switch (ptype) { + case T_OBJECT: + if (argument != NULL) + // we must implicitly convert from the arg type to the outgoing ptype + err = check_argument_type_change(T_OBJECT, argument->klass(), ptype, ptype_klass(), argnum); + break; + + case T_ARRAY: case T_VOID: + assert(false, "array, void do not appear here"); + default: + if (ptype != T_INT && !is_subword_type(ptype)) { + err = "unexpected parameter type"; + break; + } + // check subrange of Integer.value, if necessary + if (argument == NULL || argument->klass() != SystemDictionary::int_klass()) { + err = "bound integer argument must be of type java.lang.Integer"; + break; + } + if (ptype != T_INT) { + int value_offset = java_lang_boxing_object::value_offset_in_bytes(T_INT); + jint value = argument->int_field(value_offset); + int vminfo = adapter_subword_vminfo(ptype); + jint subword = truncate_subword_from_vminfo(value, vminfo); + if (value != subword) { + err = "bound subword value does not fit into the subword type"; + break; + } + } + break; + case T_FLOAT: + case T_DOUBLE: + case T_LONG: + { + // we must implicitly convert from the unboxed arg type to the outgoing ptype + BasicType argbox = java_lang_boxing_object::basic_type(argument); + if (argbox != ptype) { + err = check_argument_type_change(T_OBJECT, (argument == NULL + ? SystemDictionary::object_klass() + : argument->klass()), + ptype, ptype_klass(), argnum); + assert(err != NULL, "this must be an error"); + } + break; + } + } + + if (err == NULL) { + DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh())); + if (direct_to_method) { + assert(this_pushes == slots_pushed, "BMH pushes one or two stack slots"); + assert(slots_pushed <= MethodHandlePushLimit, ""); + } else { + int prev_pushes = decode_MethodHandle_stack_pushes(target()); + assert(this_pushes == slots_pushed + prev_pushes, "BMH stack motion must be correct"); + // do not blow the stack; use a Java-based adapter if this limit is exceeded + if (slots_pushed + prev_pushes > MethodHandlePushLimit) + err = "too many bound parameters"; + } + } + + if (err == NULL) { + // Verify the rest of the method type. + err = check_method_type_insertion(java_dyn_MethodHandle::type(mh()), + argnum, ptype_handle(), + java_dyn_MethodHandle::type(target())); + } + + if (err != NULL) { + THROW_MSG(vmSymbols::java_lang_InternalError(), err); + } +} + +void MethodHandles::init_BoundMethodHandle(Handle mh, Handle target, int argnum, TRAPS) { + // Check arguments. + if (mh.is_null() || target.is_null() || !java_dyn_MethodHandle::is_instance(target())) { + THROW(vmSymbols::java_lang_InternalError()); + } + + java_dyn_MethodHandle::init_vmslots(mh()); + + if (VerifyMethodHandles) { + int insert_after = argnum - 1; + verify_vmargslot(mh, insert_after, sun_dyn_BoundMethodHandle::vmargslot(mh()), CHECK); + verify_vmslots(mh, CHECK); + } + + // If (a) the target is a direct non-dispatched method handle, + // or (b) the target is a dispatched direct method handle and we + // are binding the receiver, cut out the middle-man. + // Do this by decoding the DMH and using its methodOop directly as vmtarget. + bool direct_to_method = false; + if (OptimizeMethodHandles && + target->klass() == SystemDictionary::DirectMethodHandle_klass() && + (argnum == 0 || sun_dyn_DirectMethodHandle::vmindex(target()) < 0)) { + int decode_flags = 0; klassOop receiver_limit_oop = NULL; + methodHandle m(THREAD, decode_method(target(), receiver_limit_oop, decode_flags)); + if (m.is_null()) { THROW_MSG(vmSymbols::java_lang_InternalError(), "DMH failed to decode"); } + DEBUG_ONLY(int m_vmslots = m->size_of_parameters() - 1); // pos. of 1st arg. + assert(sun_dyn_BoundMethodHandle::vmslots(mh()) == m_vmslots, "type w/ m sig"); + if (argnum == 0 && (decode_flags & _dmf_has_receiver) != 0) { + KlassHandle receiver_limit(THREAD, receiver_limit_oop); + init_BoundMethodHandle_with_receiver(mh, m, + receiver_limit, decode_flags, + CHECK); + return; + } + + // Even if it is not a bound receiver, we still might be able + // to bind another argument and still invoke the methodOop directly. + if (!(decode_flags & _dmf_does_dispatch)) { + direct_to_method = true; + sun_dyn_BoundMethodHandle::set_vmtarget(mh(), m()); + } + } + if (!direct_to_method) + sun_dyn_BoundMethodHandle::set_vmtarget(mh(), target()); + + if (VerifyMethodHandles) { + verify_BoundMethodHandle(mh, target, argnum, direct_to_method, CHECK); + } + + // Next question: Is this a ref, int, or long bound value? + oop ptype_oop = java_dyn_MethodType::ptype(java_dyn_MethodHandle::type(target()), argnum); + BasicType ptype = java_lang_Class::as_BasicType(ptype_oop); + int slots_pushed = type2size[ptype]; + + MethodHandleEntry* me = NULL; + if (ptype == T_OBJECT) { + if (direct_to_method) me = MethodHandles::entry(_bound_ref_direct_mh); + else me = MethodHandles::entry(_bound_ref_mh); + } else if (slots_pushed == 2) { + if (direct_to_method) me = MethodHandles::entry(_bound_long_direct_mh); + else me = MethodHandles::entry(_bound_long_mh); + } else if (slots_pushed == 1) { + if (direct_to_method) me = MethodHandles::entry(_bound_int_direct_mh); + else me = MethodHandles::entry(_bound_int_mh); + } else { + assert(false, ""); + } + + // Done! + java_dyn_MethodHandle::set_vmentry(mh(), me); +} + +static void throw_InternalError_for_bad_conversion(int conversion, const char* err, TRAPS) { + char msg[200]; + jio_snprintf(msg, sizeof(msg), "bad adapter (conversion=0x%08x): %s", conversion, err); + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), msg); +} + +void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) { + jint conversion = sun_dyn_AdapterMethodHandle::conversion(mh()); + int argslot = sun_dyn_AdapterMethodHandle::vmargslot(mh()); + + verify_vmargslot(mh, argnum, argslot, CHECK); + verify_vmslots(mh, CHECK); + + jint conv_op = adapter_conversion_op(conversion); + if (!conv_op_valid(conv_op)) { + throw_InternalError_for_bad_conversion(conversion, "unknown conversion op", THREAD); + return; + } + EntryKind ek = adapter_entry_kind(conv_op); + + int stack_move = adapter_conversion_stack_move(conversion); + BasicType src = adapter_conversion_src_type(conversion); + BasicType dest = adapter_conversion_dest_type(conversion); + int vminfo = adapter_conversion_vminfo(conversion); // should be zero + + Handle argument(THREAD, sun_dyn_AdapterMethodHandle::argument(mh())); + Handle target(THREAD, sun_dyn_AdapterMethodHandle::vmtarget(mh())); + Handle src_mtype(THREAD, java_dyn_MethodHandle::type(mh())); + Handle dst_mtype(THREAD, java_dyn_MethodHandle::type(target())); + + const char* err = NULL; + + if (err == NULL) { + // Check that the correct argument is supplied, but only if it is required. + switch (ek) { + case _adapter_check_cast: // target type of cast + case _adapter_ref_to_prim: // wrapper type from which to unbox + case _adapter_prim_to_ref: // wrapper type to box into + case _adapter_collect_args: // array type to collect into + case _adapter_spread_args: // array type to spread from + if (!java_lang_Class::is_instance(argument()) + || java_lang_Class::is_primitive(argument())) + { err = "adapter requires argument of type java.lang.Class"; break; } + if (ek == _adapter_collect_args || + ek == _adapter_spread_args) { + // Make sure it is a suitable collection type. (Array, for now.) + Klass* ak = Klass::cast(java_lang_Class::as_klassOop(argument())); + if (!ak->oop_is_objArray()) { + { err = "adapter requires argument of type java.lang.Class"; break; } + } + } + break; + case _adapter_flyby: + case _adapter_ricochet: + if (!java_dyn_MethodHandle::is_instance(argument())) + { err = "MethodHandle adapter argument required"; break; } + break; + default: + if (argument.not_null()) + { err = "adapter has spurious argument"; break; } + break; + } + } + + if (err == NULL) { + // Check that the src/dest types are supplied if needed. + switch (ek) { + case _adapter_prim_to_prim: + if (!is_java_primitive(src) || !is_java_primitive(dest) || src == dest) { + err = "adapter requires primitive src/dest conversion subfields"; break; + } + if ( (src == T_FLOAT || src == T_DOUBLE) && !(dest == T_FLOAT || dest == T_DOUBLE) || + !(src == T_FLOAT || src == T_DOUBLE) && (dest == T_FLOAT || dest == T_DOUBLE)) { + err = "adapter cannot convert beween floating and fixed-point"; break; + } + break; + case _adapter_ref_to_prim: + if (src != T_OBJECT || !is_java_primitive(dest) + || argument() != Klass::cast(SystemDictionary::box_klass(dest))->java_mirror()) { + err = "adapter requires primitive dest conversion subfield"; break; + } + break; + case _adapter_prim_to_ref: + if (!is_java_primitive(src) || dest != T_OBJECT + || argument() != Klass::cast(SystemDictionary::box_klass(src))->java_mirror()) { + err = "adapter requires primitive src conversion subfield"; break; + } + break; + case _adapter_swap_args: + case _adapter_rot_args: + { + if (!src || src != dest) { + err = "adapter requires src/dest conversion subfields for swap"; break; + } + int swap_size = type2size[src]; + oop src_mtype = sun_dyn_AdapterMethodHandle::type(target()); + oop dest_mtype = sun_dyn_AdapterMethodHandle::type(mh()); + int slot_limit = sun_dyn_AdapterMethodHandle::vmslots(src_mtype); + int src_slot = argslot; + int dest_slot = vminfo; + bool rotate_up = (src_slot > dest_slot); // upward rotation + int src_arg = argnum; + int dest_arg = argument_slot_to_argnum(dest_mtype, dest_slot); + verify_vmargslot(mh, dest_arg, dest_slot, CHECK); + if (!(dest_slot >= src_slot + swap_size) && + !(src_slot >= dest_slot + swap_size)) { + err = "source, destination slots must be distinct"; + } else if (ek == _adapter_swap_args && !(src_slot > dest_slot)) { + err = "source of swap must be deeper in stack"; + } else if (ek == _adapter_swap_args) { + err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype, dest_arg), + java_dyn_MethodType::ptype(dest_mtype, src_arg), + dest_arg); + } else if (ek == _adapter_rot_args) { + if (rotate_up) { + assert((src_slot > dest_slot) && (src_arg < dest_arg), ""); + // rotate up: [dest_slot..src_slot-ss] --> [dest_slot+ss..src_slot] + // that is: [src_arg+1..dest_arg] --> [src_arg..dest_arg-1] + for (int i = src_arg+1; i <= dest_arg && err == NULL; i++) { + err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype, i), + java_dyn_MethodType::ptype(dest_mtype, i-1), + i); + } + } else { // rotate down + assert((src_slot < dest_slot) && (src_arg > dest_arg), ""); + // rotate down: [src_slot+ss..dest_slot] --> [src_slot..dest_slot-ss] + // that is: [dest_arg..src_arg-1] --> [dst_arg+1..src_arg] + for (int i = dest_arg; i <= src_arg-1 && err == NULL; i++) { + err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype, i), + java_dyn_MethodType::ptype(dest_mtype, i+1), + i); + } + } + } + if (err == NULL) + err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype, src_arg), + java_dyn_MethodType::ptype(dest_mtype, dest_arg), + src_arg); + } + break; + case _adapter_collect_args: + case _adapter_spread_args: + { + BasicType coll_type = (ek == _adapter_collect_args) ? dest : src; + BasicType elem_type = (ek == _adapter_collect_args) ? src : dest; + if (coll_type != T_OBJECT || elem_type != T_OBJECT) { + err = "adapter requires src/dest subfields"; break; + // later: + // - consider making coll be a primitive array + // - consider making coll be a heterogeneous collection + } + } + break; + default: + if (src != 0 || dest != 0) { + err = "adapter has spurious src/dest conversion subfields"; break; + } + break; + } + } + + if (err == NULL) { + // Check the stack_move subfield. + // It must always report the net change in stack size, positive or negative. + int slots_pushed = stack_move / stack_move_unit(); + switch (ek) { + case _adapter_prim_to_prim: + case _adapter_ref_to_prim: + case _adapter_prim_to_ref: + if (slots_pushed != type2size[dest] - type2size[src]) { + err = "wrong stack motion for primitive conversion"; + } + break; + case _adapter_dup_args: + if (slots_pushed <= 0) { + err = "adapter requires conversion subfield slots_pushed > 0"; + } + break; + case _adapter_drop_args: + if (slots_pushed >= 0) { + err = "adapter requires conversion subfield slots_pushed < 0"; + } + break; + case _adapter_collect_args: + if (slots_pushed > 1) { + err = "adapter requires conversion subfield slots_pushed <= 1"; + } + break; + case _adapter_spread_args: + if (slots_pushed < -1) { + err = "adapter requires conversion subfield slots_pushed >= -1"; + } + break; + default: + if (stack_move != 0) { + err = "adapter has spurious stack_move conversion subfield"; + } + break; + } + if (err == NULL && stack_move != slots_pushed * stack_move_unit()) { + err = "stack_move conversion subfield must be multiple of stack_move_unit"; + } + } + + if (err == NULL) { + // Make sure this adapter does not push too deeply. + int slots_pushed = stack_move / stack_move_unit(); + int this_vmslots = java_dyn_MethodHandle::vmslots(mh()); + int prev_vmslots = java_dyn_MethodHandle::vmslots(target()); + if (slots_pushed != (this_vmslots - prev_vmslots)) { + err = "stack_move inconsistent with previous and current MethodType vmslots"; + } else if (slots_pushed > 0) { + // verify stack_move against MethodHandlePushLimit + int prev_pushes = decode_MethodHandle_stack_pushes(target()); + // do not blow the stack; use a Java-based adapter if this limit is exceeded + if (slots_pushed + prev_pushes > MethodHandlePushLimit) { + err = "adapter pushes too many parameters"; + } + } + + // While we're at it, check that the stack motion decoder works: + DEBUG_ONLY(int prev_pushes = decode_MethodHandle_stack_pushes(target())); + DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh())); + assert(this_pushes == slots_pushed + prev_pushes, "AMH stack motion must be correct"); + } + + if (err == NULL && vminfo != 0) { + switch (ek) { + case _adapter_swap_args: + case _adapter_rot_args: + break; // OK + default: + err = "vminfo subfield is reserved to the JVM"; + } + } + + // Do additional ad hoc checks. + if (err == NULL) { + switch (ek) { + case _adapter_retype_only: + err = check_method_type_passthrough(src_mtype(), dst_mtype()); + break; + + case _adapter_check_cast: + { + // The actual value being checked must be a reference: + err = check_argument_type_change(java_dyn_MethodType::ptype(src_mtype(), argnum), + object_java_mirror(), argnum); + if (err != NULL) break; + + // The output of the cast must fit with the destination argument: + Handle cast_class = argument; + err = check_method_type_conversion(src_mtype(), + argnum, cast_class(), + dst_mtype()); + } + break; + + // %%% TO DO: continue in remaining cases to verify src/dst_mtype if VerifyMethodHandles + } + } + + if (err != NULL) { + throw_InternalError_for_bad_conversion(conversion, err, THREAD); + return; + } + +} + +void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS) { + oop argument = sun_dyn_AdapterMethodHandle::argument(mh()); + int argslot = sun_dyn_AdapterMethodHandle::vmargslot(mh()); + jint conversion = sun_dyn_AdapterMethodHandle::conversion(mh()); + jint conv_op = adapter_conversion_op(conversion); + + // adjust the adapter code to the internal EntryKind enumeration: + EntryKind ek_orig = adapter_entry_kind(conv_op); + EntryKind ek_opt = ek_orig; // may be optimized + + // Finalize the vmtarget field (Java initialized it to null). + if (!java_dyn_MethodHandle::is_instance(target())) { + throw_InternalError_for_bad_conversion(conversion, "bad target", THREAD); + return; + } + sun_dyn_AdapterMethodHandle::set_vmtarget(mh(), target()); + + if (VerifyMethodHandles) { + verify_AdapterMethodHandle(mh, argnum, CHECK); + } + + int stack_move = adapter_conversion_stack_move(conversion); + BasicType src = adapter_conversion_src_type(conversion); + BasicType dest = adapter_conversion_dest_type(conversion); + int vminfo = adapter_conversion_vminfo(conversion); // should be zero + + const char* err = NULL; + + // Now it's time to finish the case analysis and pick a MethodHandleEntry. + switch (ek_orig) { + case _adapter_retype_only: + case _adapter_check_cast: + case _adapter_dup_args: + case _adapter_drop_args: + // these work fine via general case code + break; + + case _adapter_prim_to_prim: + { + // Non-subword cases are {int,float,long,double} -> {int,float,long,double}. + // And, the {float,double} -> {int,long} cases must be handled by Java. + switch (type2size[src] *4+ type2size[dest]) { + case 1 *4+ 1: + assert(src == T_INT || is_subword_type(src), "source is not float"); + // Subword-related cases are int -> {boolean,byte,char,short}. + ek_opt = _adapter_opt_i2i; + vminfo = adapter_subword_vminfo(dest); + break; + case 2 *4+ 1: + if (src == T_LONG && (dest == T_INT || is_subword_type(dest))) { + ek_opt = _adapter_opt_l2i; + vminfo = adapter_subword_vminfo(dest); + } else if (src == T_DOUBLE && dest == T_FLOAT) { + ek_opt = _adapter_opt_d2f; + } else { + assert(false, ""); + } + break; + case 1 *4+ 2: + if (src == T_INT && dest == T_LONG) { + ek_opt = _adapter_opt_i2l; + } else if (src == T_FLOAT && dest == T_DOUBLE) { + ek_opt = _adapter_opt_f2d; + } else { + assert(false, ""); + } + break; + default: + assert(false, ""); + break; + } + } + break; + + case _adapter_ref_to_prim: + { + switch (type2size[dest]) { + case 1: + ek_opt = _adapter_opt_unboxi; + vminfo = adapter_subword_vminfo(dest); + break; + case 2: + ek_opt = _adapter_opt_unboxl; + break; + default: + assert(false, ""); + break; + } + } + break; + + case _adapter_prim_to_ref: + goto throw_not_impl; // allocates, hence could block + + case _adapter_swap_args: + case _adapter_rot_args: + { + int swap_slots = type2size[src]; + oop mtype = sun_dyn_AdapterMethodHandle::type(mh()); + int slot_limit = sun_dyn_AdapterMethodHandle::vmslots(mtype); + int src_slot = argslot; + int dest_slot = vminfo; + int rotate = (ek_orig == _adapter_swap_args) ? 0 : (src_slot > dest_slot) ? 1 : -1; + switch (swap_slots) { + case 1: + ek_opt = (!rotate ? _adapter_opt_swap_1 : + rotate > 0 ? _adapter_opt_rot_1_up : _adapter_opt_rot_1_down); + break; + case 2: + ek_opt = (!rotate ? _adapter_opt_swap_2 : + rotate > 0 ? _adapter_opt_rot_2_up : _adapter_opt_rot_2_down); + break; + default: + assert(false, ""); + break; + } + } + break; + + case _adapter_collect_args: + goto throw_not_impl; // allocates, hence could block + + case _adapter_spread_args: + { + // vminfo will be the required length of the array + int slots_pushed = stack_move / stack_move_unit(); + int array_size = slots_pushed + 1; + assert(array_size >= 0, ""); + vminfo = array_size; + switch (array_size) { + case 0: ek_opt = _adapter_opt_spread_0; break; + case 1: ek_opt = _adapter_opt_spread_1; break; + default: ek_opt = _adapter_opt_spread_more; break; + } + if ((vminfo & CONV_VMINFO_MASK) != vminfo) + goto throw_not_impl; // overflow + } + break; + + case _adapter_flyby: + case _adapter_ricochet: + goto throw_not_impl; // runs Java code, hence could block + + default: + // should have failed much earlier; must be a missing case here + assert(false, "incomplete switch"); + // and fall through: + + throw_not_impl: + // FIXME: these adapters are NYI + err = "adapter not yet implemented in the JVM"; + break; + } + + if (err != NULL) { + throw_InternalError_for_bad_conversion(conversion, err, THREAD); + return; + } + + // Rebuild the conversion value; maybe parts of it were changed. + jint new_conversion = adapter_conversion(conv_op, src, dest, stack_move, vminfo); + + // Finalize the conversion field. (Note that it is final to Java code.) + sun_dyn_AdapterMethodHandle::set_conversion(mh(), new_conversion); + + // Done! + java_dyn_MethodHandle::set_vmentry(mh(), entry(ek_opt)); + + // There should be enough memory barriers on exit from native methods + // to ensure that the MH is fully initialized to all threads before + // Java code can publish it in global data structures. +} + +// +// Here are the native methods on sun.dyn.MethodHandleImpl. +// They are the private interface between this JVM and the HotSpot-specific +// Java code that implements JSR 292 method handles. +// +// Note: We use a JVM_ENTRY macro to define each of these, for this is the way +// that intrinsic (non-JNI) native methods are defined in HotSpot. +// + +// direct method handles for invokestatic or invokespecial +// void init(DirectMethodHandle self, MemberName ref, boolean doDispatch, Class caller); +JVM_ENTRY(void, MHI_init_DMH(JNIEnv *env, jobject igcls, jobject mh_jh, + jobject target_jh, jboolean do_dispatch, jobject caller_jh)) { + ResourceMark rm; // for error messages + + // This is the guy we are initializing: + if (mh_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); } + Handle mh(THREAD, JNIHandles::resolve_non_null(mh_jh)); + + // Early returns out of this method leave the DMH in an unfinished state. + assert(java_dyn_MethodHandle::vmentry(mh()) == NULL, "must be safely null"); + + // which method are we really talking about? + if (target_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); } + oop target_oop = JNIHandles::resolve_non_null(target_jh); + if (sun_dyn_MemberName::is_instance(target_oop) && + sun_dyn_MemberName::vmindex(target_oop) == VM_INDEX_UNINITIALIZED) { + Handle mname(THREAD, target_oop); + MethodHandles::resolve_MemberName(mname, CHECK); + target_oop = mname(); // in case of GC + } + + int decode_flags = 0; klassOop receiver_limit = NULL; + methodHandle m(THREAD, + MethodHandles::decode_method(target_oop, + receiver_limit, decode_flags)); + if (m.is_null()) { THROW_MSG(vmSymbols::java_lang_InternalError(), "no such method"); } + + // The trusted Java code that calls this method should already have performed + // access checks on behalf of the given caller. But, we can verify this. + if (VerifyMethodHandles && caller_jh != NULL) { + KlassHandle caller(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(caller_jh))); + // If this were a bytecode, the first access check would be against + // the "reference class" mentioned in the CONSTANT_Methodref. + // For that class, we use the defining class of m, + // or a more specific receiver limit if available. + klassOop reference_klass = m->method_holder(); // OK approximation + if (receiver_limit != NULL && receiver_limit != reference_klass) { + if (!Klass::cast(receiver_limit)->is_subtype_of(reference_klass)) + THROW_MSG(vmSymbols::java_lang_InternalError(), "receiver limit out of bounds"); // Java code bug + reference_klass = receiver_limit; + } + // Emulate LinkResolver::check_klass_accessability. + if (!Reflection::verify_class_access(caller->as_klassOop(), + reference_klass, + true)) { + THROW_MSG(vmSymbols::java_lang_InternalError(), Klass::cast(m->method_holder())->external_name()); + } + // If there were a bytecode, the next step would be to lookup the method + // in the reference class, then then check the method's access bits. + // Emulate LinkResolver::check_method_accessability. + klassOop resolved_klass = m->method_holder(); + if (!Reflection::verify_field_access(caller->as_klassOop(), + resolved_klass, reference_klass, + m->access_flags(), + true)) { + // %%% following cutout belongs in Reflection::verify_field_access? + bool same_pm = Reflection::is_same_package_member(caller->as_klassOop(), + reference_klass, THREAD); + if (!same_pm) { + THROW_MSG(vmSymbols::java_lang_InternalError(), m->name_and_sig_as_C_string()); + } + } + } + + MethodHandles::init_DirectMethodHandle(mh, m, (do_dispatch != JNI_FALSE), CHECK); +} +JVM_END + +// bound method handles +JVM_ENTRY(void, MHI_init_BMH(JNIEnv *env, jobject igcls, jobject mh_jh, + jobject target_jh, int argnum)) { + ResourceMark rm; // for error messages + + // This is the guy we are initializing: + if (mh_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); } + Handle mh(THREAD, JNIHandles::resolve_non_null(mh_jh)); + + // Early returns out of this method leave the BMH in an unfinished state. + assert(java_dyn_MethodHandle::vmentry(mh()) == NULL, "must be safely null"); + + if (target_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); } + Handle target(THREAD, JNIHandles::resolve_non_null(target_jh)); + + if (!java_dyn_MethodHandle::is_instance(target())) { + // Target object is a reflective method. (%%% Do we need this alternate path?) + Untested("init_BMH of non-MH"); + if (argnum != 0) { THROW(vmSymbols::java_lang_InternalError()); } + int decode_flags = 0; klassOop receiver_limit_oop = NULL; + methodHandle m(THREAD, + MethodHandles::decode_method(target(), + receiver_limit_oop, + decode_flags)); + KlassHandle receiver_limit(THREAD, receiver_limit_oop); + MethodHandles::init_BoundMethodHandle_with_receiver(mh, m, + receiver_limit, + decode_flags, + CHECK); + return; + } + + // Build a BMH on top of a DMH or another BMH: + MethodHandles::init_BoundMethodHandle(mh, target, argnum, CHECK); +} +JVM_END + +// adapter method handles +JVM_ENTRY(void, MHI_init_AMH(JNIEnv *env, jobject igcls, jobject mh_jh, + jobject target_jh, int argnum)) { + // This is the guy we are initializing: + if (mh_jh == NULL || target_jh == NULL) { + THROW(vmSymbols::java_lang_InternalError()); + } + Handle mh(THREAD, JNIHandles::resolve_non_null(mh_jh)); + Handle target(THREAD, JNIHandles::resolve_non_null(target_jh)); + + // Early returns out of this method leave the AMH in an unfinished state. + assert(java_dyn_MethodHandle::vmentry(mh()) == NULL, "must be safely null"); + + MethodHandles::init_AdapterMethodHandle(mh, target, argnum, CHECK); +} +JVM_END + +// method type forms +JVM_ENTRY(void, MHI_init_MT(JNIEnv *env, jobject igcls, jobject erased_jh)) { + if (erased_jh == NULL) return; + if (TraceMethodHandles) { + tty->print("creating MethodType form "); + if (WizardMode || Verbose) { // Warning: this calls Java code on the MH! + // call Object.toString() + symbolOop name = vmSymbols::toString_name(), sig = vmSymbols::void_string_signature(); + JavaCallArguments args(Handle(THREAD, JNIHandles::resolve_non_null(erased_jh))); + JavaValue result(T_OBJECT); + JavaCalls::call_virtual(&result, SystemDictionary::object_klass(), name, sig, + &args, CHECK); + Handle str(THREAD, (oop)result.get_jobject()); + java_lang_String::print(str, tty); + } + tty->cr(); + } +} +JVM_END + +// debugging and reflection +JVM_ENTRY(jobject, MHI_getTarget(JNIEnv *env, jobject igcls, jobject mh_jh, jint format)) { + Handle mh(THREAD, JNIHandles::resolve(mh_jh)); + if (!java_dyn_MethodHandle::is_instance(mh())) { + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); + } + oop target = MethodHandles::encode_target(mh, format, CHECK_NULL); + return JNIHandles::make_local(THREAD, target); +} +JVM_END + +JVM_ENTRY(jint, MHI_getConstant(JNIEnv *env, jobject igcls, jint which)) { + switch (which) { + case MethodHandles::GC_JVM_PUSH_LIMIT: + guarantee(MethodHandlePushLimit >= 2 && MethodHandlePushLimit <= 0xFF, + "MethodHandlePushLimit parameter must be in valid range"); + return MethodHandlePushLimit; + case MethodHandles::GC_JVM_STACK_MOVE_LIMIT: + // return number of words per slot, signed according to stack direction + return MethodHandles::stack_move_unit(); + } + return 0; +} +JVM_END + +#ifndef PRODUCT +#define EACH_NAMED_CON(template) \ + template(MethodHandles,GC_JVM_PUSH_LIMIT) \ + template(MethodHandles,GC_JVM_STACK_MOVE_LIMIT) \ + template(MethodHandles,ETF_HANDLE_OR_METHOD_NAME) \ + template(MethodHandles,ETF_DIRECT_HANDLE) \ + template(MethodHandles,ETF_METHOD_NAME) \ + template(MethodHandles,ETF_REFLECT_METHOD) \ + template(sun_dyn_MemberName,MN_IS_METHOD) \ + template(sun_dyn_MemberName,MN_IS_CONSTRUCTOR) \ + template(sun_dyn_MemberName,MN_IS_FIELD) \ + template(sun_dyn_MemberName,MN_IS_TYPE) \ + template(sun_dyn_MemberName,MN_SEARCH_SUPERCLASSES) \ + template(sun_dyn_MemberName,MN_SEARCH_INTERFACES) \ + template(sun_dyn_MemberName,VM_INDEX_UNINITIALIZED) \ + template(sun_dyn_AdapterMethodHandle,OP_RETYPE_ONLY) \ + template(sun_dyn_AdapterMethodHandle,OP_CHECK_CAST) \ + template(sun_dyn_AdapterMethodHandle,OP_PRIM_TO_PRIM) \ + template(sun_dyn_AdapterMethodHandle,OP_REF_TO_PRIM) \ + template(sun_dyn_AdapterMethodHandle,OP_PRIM_TO_REF) \ + template(sun_dyn_AdapterMethodHandle,OP_SWAP_ARGS) \ + template(sun_dyn_AdapterMethodHandle,OP_ROT_ARGS) \ + template(sun_dyn_AdapterMethodHandle,OP_DUP_ARGS) \ + template(sun_dyn_AdapterMethodHandle,OP_DROP_ARGS) \ + template(sun_dyn_AdapterMethodHandle,OP_COLLECT_ARGS) \ + template(sun_dyn_AdapterMethodHandle,OP_SPREAD_ARGS) \ + template(sun_dyn_AdapterMethodHandle,OP_FLYBY) \ + template(sun_dyn_AdapterMethodHandle,OP_RICOCHET) \ + template(sun_dyn_AdapterMethodHandle,CONV_OP_LIMIT) \ + template(sun_dyn_AdapterMethodHandle,CONV_OP_MASK) \ + template(sun_dyn_AdapterMethodHandle,CONV_VMINFO_MASK) \ + template(sun_dyn_AdapterMethodHandle,CONV_VMINFO_SHIFT) \ + template(sun_dyn_AdapterMethodHandle,CONV_OP_SHIFT) \ + template(sun_dyn_AdapterMethodHandle,CONV_DEST_TYPE_SHIFT) \ + template(sun_dyn_AdapterMethodHandle,CONV_SRC_TYPE_SHIFT) \ + template(sun_dyn_AdapterMethodHandle,CONV_STACK_MOVE_SHIFT) \ + template(sun_dyn_AdapterMethodHandle,CONV_STACK_MOVE_MASK) \ + /*end*/ + +#define ONE_PLUS(scope,value) 1+ +static const int con_value_count = EACH_NAMED_CON(ONE_PLUS) 0; +#define VALUE_COMMA(scope,value) scope::value, +static const int con_values[con_value_count+1] = { EACH_NAMED_CON(VALUE_COMMA) 0 }; +#define STRING_NULL(scope,value) #value "\0" +static const char con_names[] = { EACH_NAMED_CON(STRING_NULL) }; + +#undef ONE_PLUS +#undef VALUE_COMMA +#undef STRING_NULL +#undef EACH_NAMED_CON +#endif + +JVM_ENTRY(jint, MHI_getNamedCon(JNIEnv *env, jobject igcls, jint which, jobjectArray box_jh)) { +#ifndef PRODUCT + if (which >= 0 && which < con_value_count) { + int con = con_values[which]; + objArrayOop box = (objArrayOop) JNIHandles::resolve(box_jh); + if (box != NULL && box->klass() == Universe::objectArrayKlassObj() && box->length() > 0) { + const char* str = &con_names[0]; + for (int i = 0; i < which; i++) + str += strlen(str) + 1; // skip name and null + oop name = java_lang_String::create_oop_from_str(str, CHECK_0); + box->obj_at_put(0, name); + } + return con; + } +#endif + return 0; +} +JVM_END + +// void init(MemberName self, AccessibleObject ref) +JVM_ENTRY(void, MHI_init_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jobject target_jh)) { + if (mname_jh == NULL || target_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); } + Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh)); + oop target_oop = JNIHandles::resolve_non_null(target_jh); + MethodHandles::init_MemberName(mname(), target_oop); +} +JVM_END + +// void expand(MemberName self) +JVM_ENTRY(void, MHI_expand_Mem(JNIEnv *env, jobject igcls, jobject mname_jh)) { + if (mname_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); } + Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh)); + MethodHandles::expand_MemberName(mname, 0, CHECK); +} +JVM_END + +// void resolve(MemberName self, Class caller) +JVM_ENTRY(void, MHI_resolve_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jclass caller_jh)) { + if (mname_jh == NULL) { THROW(vmSymbols::java_lang_InternalError()); } + Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh)); + // %%% take caller into account! + MethodHandles::resolve_MemberName(mname, CHECK); +} +JVM_END + +// static native int getMembers(Class defc, String matchName, String matchSig, +// int matchFlags, Class caller, int skip, MemberName[] results); +JVM_ENTRY(jint, MHI_getMembers(JNIEnv *env, jobject igcls, + jclass clazz_jh, jstring name_jh, jstring sig_jh, + int mflags, jclass caller_jh, jint skip, jobjectArray results_jh)) { + if (clazz_jh == NULL || results_jh == NULL) return -1; + klassOop k_oop = java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(clazz_jh)); + + objArrayOop results = (objArrayOop) JNIHandles::resolve(results_jh); + if (results == NULL || !results->is_objArray()) return -1; + + symbolOop name = NULL, sig = NULL; + if (name_jh != NULL) { + name = java_lang_String::as_symbol_or_null(JNIHandles::resolve_non_null(name_jh)); + if (name == NULL) return 0; // a match is not possible + } + if (sig_jh != NULL) { + sig = java_lang_String::as_symbol_or_null(JNIHandles::resolve_non_null(sig_jh)); + if (sig == NULL) return 0; // a match is not possible + } + + klassOop caller = NULL; + if (caller_jh != NULL) { + oop caller_oop = JNIHandles::resolve_non_null(caller_jh); + if (!java_lang_Class::is_instance(caller_oop)) return -1; + caller = java_lang_Class::as_klassOop(caller_oop); + } + + if (name != NULL && sig != NULL && results != NULL) { + // try a direct resolve + // %%% TO DO + } + + int res = MethodHandles::find_MemberNames(k_oop, name, sig, mflags, + caller, skip, results); + // TO DO: expand at least some of the MemberNames, to avoid massive callbacks + return res; +} +JVM_END + + +/// JVM_RegisterMethodHandleMethods + +#define ADR "J" + +#define LANG "Ljava/lang/" +#define JDYN "Ljava/dyn/" +#define IDYN "Lsun/dyn/" + +#define OBJ LANG"Object;" +#define CLS LANG"Class;" +#define STRG LANG"String;" +#define MT JDYN"MethodType;" +#define MH JDYN"MethodHandle;" +#define MHI IDYN"MethodHandleImpl;" +#define MEM IDYN"MemberName;" +#define AMH IDYN"AdapterMethodHandle;" +#define BMH IDYN"BoundMethodHandle;" +#define DMH IDYN"DirectMethodHandle;" + +#define CC (char*) /*cast a literal from (const char*)*/ +#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) + +// These are the native methods on sun.dyn.MethodHandleNatives. +static JNINativeMethod methods[] = { + // void init(MemberName self, AccessibleObject ref) + {CC"init", CC"("AMH""MH"I)V", FN_PTR(MHI_init_AMH)}, + {CC"init", CC"("BMH""OBJ"I)V", FN_PTR(MHI_init_BMH)}, + {CC"init", CC"("DMH""OBJ"Z"CLS")V", FN_PTR(MHI_init_DMH)}, + {CC"init", CC"("MT")V", FN_PTR(MHI_init_MT)}, + {CC"init", CC"("MEM""OBJ")V", FN_PTR(MHI_init_Mem)}, + {CC"expand", CC"("MEM")V", FN_PTR(MHI_expand_Mem)}, + {CC"resolve", CC"("MEM""CLS")V", FN_PTR(MHI_resolve_Mem)}, + {CC"getTarget", CC"("MH"I)"OBJ, FN_PTR(MHI_getTarget)}, + {CC"getConstant", CC"(I)I", FN_PTR(MHI_getConstant)}, + // static native int getNamedCon(int which, Object[] name) + {CC"getNamedCon", CC"(I["OBJ")I", FN_PTR(MHI_getNamedCon)}, + // static native int getMembers(Class defc, String matchName, String matchSig, + // int matchFlags, Class caller, int skip, MemberName[] results); + {CC"getMembers", CC"("CLS""STRG""STRG"I"CLS"I["MEM")I", FN_PTR(MHI_getMembers)} +}; + + +// This one function is exported, used by NativeLookup. + +JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) { + assert(MethodHandles::spot_check_entry_names(), "entry enum is OK"); + + if (!EnableMethodHandles) { + warning("JSR 292 method handles are disabled in this JVM. Use -XX:+EnableMethodHandles to enable."); + return; // bind nothing + } + + { + ThreadToNativeFromVM ttnfv(thread); + + int status = env->RegisterNatives(MHN_class, methods, sizeof(methods)/sizeof(JNINativeMethod)); + if (env->ExceptionOccurred()) { + MethodHandles::set_enabled(false); + warning("JSR 292 method handle code is mismatched to this JVM. Disabling support."); + env->ExceptionClear(); + } else { + MethodHandles::set_enabled(true); + } + } +} +JVM_END diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/prims/methodHandles.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/prims/methodHandles.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -0,0 +1,435 @@ +/* + * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class MacroAssembler; +class Label; +class MethodHandleEntry; + +class MethodHandles: AllStatic { + // JVM support for MethodHandle, MethodType, and related types + // in java.dyn and java.dyn.hotspot. + // See also javaClasses for layouts java_dyn_Method{Handle,Type,Type::Form}. + public: + enum EntryKind { + _check_mtype, // how a caller calls a MH + _wrong_method_type, // what happens when there is a type mismatch + _invokestatic_mh, // how a MH emulates invokestatic + _invokespecial_mh, // ditto for the other invokes... + _invokevirtual_mh, + _invokeinterface_mh, + _bound_ref_mh, // reference argument is bound + _bound_int_mh, // int argument is bound (via an Integer or Float) + _bound_long_mh, // long argument is bound (via a Long or Double) + _bound_ref_direct_mh, // same as above, with direct linkage to methodOop + _bound_int_direct_mh, + _bound_long_direct_mh, + + _adapter_mh_first, // adapter sequence goes here... + _adapter_retype_only = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_RETYPE_ONLY, + _adapter_check_cast = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_CHECK_CAST, + _adapter_prim_to_prim = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_PRIM_TO_PRIM, + _adapter_ref_to_prim = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_REF_TO_PRIM, + _adapter_prim_to_ref = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_PRIM_TO_REF, + _adapter_swap_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_SWAP_ARGS, + _adapter_rot_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_ROT_ARGS, + _adapter_dup_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_DUP_ARGS, + _adapter_drop_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_DROP_ARGS, + _adapter_collect_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_COLLECT_ARGS, + _adapter_spread_args = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_SPREAD_ARGS, + _adapter_flyby = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_FLYBY, + _adapter_ricochet = _adapter_mh_first + sun_dyn_AdapterMethodHandle::OP_RICOCHET, + _adapter_mh_last = _adapter_mh_first + sun_dyn_AdapterMethodHandle::CONV_OP_LIMIT - 1, + + // Optimized adapter types + + // argument list reordering + _adapter_opt_swap_1, + _adapter_opt_swap_2, + _adapter_opt_rot_1_up, + _adapter_opt_rot_1_down, + _adapter_opt_rot_2_up, + _adapter_opt_rot_2_down, + // primitive single to single: + _adapter_opt_i2i, // i2c, i2z, i2b, i2s + // primitive double to single: + _adapter_opt_l2i, + _adapter_opt_d2f, + // primitive single to double: + _adapter_opt_i2l, + _adapter_opt_f2d, + // conversion between floating point and integer type is handled by Java + + // reference to primitive: + _adapter_opt_unboxi, + _adapter_opt_unboxl, + + // spreading (array length cases 0, 1, >=2) + _adapter_opt_spread_0, + _adapter_opt_spread_1, + _adapter_opt_spread_more, + + _EK_LIMIT, + _EK_FIRST = 0 + }; + + public: + static bool enabled() { return _enabled; } + static void set_enabled(bool z); + + private: + enum { // import sun_dyn_AdapterMethodHandle::CONV_OP_* + CONV_OP_LIMIT = sun_dyn_AdapterMethodHandle::CONV_OP_LIMIT, + CONV_OP_MASK = sun_dyn_AdapterMethodHandle::CONV_OP_MASK, + CONV_VMINFO_MASK = sun_dyn_AdapterMethodHandle::CONV_VMINFO_MASK, + CONV_VMINFO_SHIFT = sun_dyn_AdapterMethodHandle::CONV_VMINFO_SHIFT, + CONV_OP_SHIFT = sun_dyn_AdapterMethodHandle::CONV_OP_SHIFT, + CONV_DEST_TYPE_SHIFT = sun_dyn_AdapterMethodHandle::CONV_DEST_TYPE_SHIFT, + CONV_SRC_TYPE_SHIFT = sun_dyn_AdapterMethodHandle::CONV_SRC_TYPE_SHIFT, + CONV_STACK_MOVE_SHIFT = sun_dyn_AdapterMethodHandle::CONV_STACK_MOVE_SHIFT, + CONV_STACK_MOVE_MASK = sun_dyn_AdapterMethodHandle::CONV_STACK_MOVE_MASK + }; + + static bool _enabled; + static MethodHandleEntry* _entries[_EK_LIMIT]; + static const char* _entry_names[_EK_LIMIT+1]; + static bool ek_valid(EntryKind ek) { return (uint)ek < (uint)_EK_LIMIT; } + static bool conv_op_valid(int op) { return (uint)op < (uint)CONV_OP_LIMIT; } + + public: + static bool have_entry(EntryKind ek) { return ek_valid(ek) && _entries[ek] != NULL; } + static MethodHandleEntry* entry(EntryKind ek) { assert(ek_valid(ek), "initialized"); + return _entries[ek]; } + static const char* entry_name(EntryKind ek) { assert(ek_valid(ek), "oob"); + return _entry_names[ek]; } + static EntryKind adapter_entry_kind(int op) { assert(conv_op_valid(op), "oob"); + return EntryKind(_adapter_mh_first + op); } + + static void init_entry(EntryKind ek, MethodHandleEntry* me) { + assert(ek_valid(ek), "oob"); + assert(_entries[ek] == NULL, "no double initialization"); + _entries[ek] = me; + } + + static jint adapter_conversion(int conv_op, BasicType src, BasicType dest, + int stack_move = 0, int vminfo = 0) { + assert(conv_op_valid(conv_op), "oob"); + jint conv = ((conv_op << CONV_OP_SHIFT) + | (src << CONV_SRC_TYPE_SHIFT) + | (dest << CONV_DEST_TYPE_SHIFT) + | (stack_move << CONV_STACK_MOVE_SHIFT) + | (vminfo << CONV_VMINFO_SHIFT) + ); + assert(adapter_conversion_op(conv) == conv_op, "decode conv_op"); + assert(adapter_conversion_src_type(conv) == src, "decode src"); + assert(adapter_conversion_dest_type(conv) == dest, "decode dest"); + assert(adapter_conversion_stack_move(conv) == stack_move, "decode stack_move"); + assert(adapter_conversion_vminfo(conv) == vminfo, "decode vminfo"); + return conv; + } + static int adapter_conversion_op(jint conv) { + return ((conv >> CONV_OP_SHIFT) & 0xF); + } + static BasicType adapter_conversion_src_type(jint conv) { + return (BasicType)((conv >> CONV_SRC_TYPE_SHIFT) & 0xF); + } + static BasicType adapter_conversion_dest_type(jint conv) { + return (BasicType)((conv >> CONV_DEST_TYPE_SHIFT) & 0xF); + } + static int adapter_conversion_stack_move(jint conv) { + return (conv >> CONV_STACK_MOVE_SHIFT); + } + static int adapter_conversion_vminfo(jint conv) { + return (conv >> CONV_VMINFO_SHIFT) & CONV_VMINFO_MASK; + } + + // Offset in words that the interpreter stack pointer moves when an argument is pushed. + // The stack_move value must always be a multiple of this. + static int stack_move_unit() { + return frame::interpreter_frame_expression_stack_direction() * Interpreter::stackElementWords(); + } + + enum { CONV_VMINFO_SIGN_FLAG = 0x80 }; + static int adapter_subword_vminfo(BasicType dest) { + if (dest == T_BOOLEAN) return (BitsPerInt - 1); + if (dest == T_CHAR) return (BitsPerInt - 16); + if (dest == T_BYTE) return (BitsPerInt - 8) | CONV_VMINFO_SIGN_FLAG; + if (dest == T_SHORT) return (BitsPerInt - 16) | CONV_VMINFO_SIGN_FLAG; + return 0; // case T_INT + } + // Here is the transformation the i2i adapter must perform: + static int truncate_subword_from_vminfo(jint value, int vminfo) { + jint tem = value << vminfo; + if ((vminfo & CONV_VMINFO_SIGN_FLAG) != 0) { + return (jint)tem >> vminfo; + } else { + return (juint)tem >> vminfo; + } + } + + static inline address from_compiled_entry(EntryKind ek); + static inline address from_interpreted_entry(EntryKind ek); + + // helpers for decode_method. + static methodOop decode_methodOop(methodOop m, int& decode_flags_result); + static methodOop decode_vmtarget(oop vmtarget, int vmindex, oop mtype, klassOop& receiver_limit_result, int& decode_flags_result); + static methodOop decode_MemberName(oop mname, klassOop& receiver_limit_result, int& decode_flags_result); + static methodOop decode_MethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result); + static methodOop decode_DirectMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result); + static methodOop decode_BoundMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result); + static methodOop decode_AdapterMethodHandle(oop mh, klassOop& receiver_limit_result, int& decode_flags_result); + + // Find out how many stack slots an mh pushes or pops. + // The result is *not* reported as a multiple of stack_move_unit(); + // It is a signed net number of pushes (a difference in vmslots). + // To compare with a stack_move value, first multiply by stack_move_unit(). + static int decode_MethodHandle_stack_pushes(oop mh); + + public: + // working with member names + static void resolve_MemberName(Handle mname, TRAPS); // compute vmtarget/vmindex from name/type + static void expand_MemberName(Handle mname, int suppress, TRAPS); // expand defc/name/type if missing + static void init_MemberName(oop mname_oop, oop target); // compute vmtarget/vmindex from target + static void init_MemberName(oop mname_oop, methodOop m, bool do_dispatch); + static void init_MemberName(oop mname_oop, klassOop field_holder, AccessFlags mods, int offset); + static int find_MemberNames(klassOop k, symbolOop name, symbolOop sig, + int mflags, klassOop caller, + int skip, objArrayOop results); + // bit values for suppress argument to expand_MemberName: + enum { _suppress_defc = 1, _suppress_name = 2, _suppress_type = 4 }; + + // called from InterpreterGenerator and StubGenerator + static address generate_method_handle_interpreter_entry(MacroAssembler* _masm); + static void generate_method_handle_stub(MacroAssembler* _masm, EntryKind ek); + + // argument list parsing + static int argument_slot(oop method_type, int arg); + static int argument_slot_count(oop method_type) { return argument_slot(method_type, -1); } + static int argument_slot_to_argnum(oop method_type, int argslot); + + // Runtime support + enum { // bit-encoded flags from decode_method or decode_vmref + _dmf_has_receiver = 0x01, // target method has leading reference argument + _dmf_does_dispatch = 0x02, // method handle performs virtual or interface dispatch + _dmf_from_interface = 0x04, // peforms interface dispatch + _DMF_DIRECT_MASK = (_dmf_from_interface*2 - _dmf_has_receiver), + _dmf_binds_method = 0x08, + _dmf_binds_argument = 0x10, + _DMF_BOUND_MASK = (_dmf_binds_argument*2 - _dmf_binds_method), + _dmf_adapter_lsb = 0x20, + _DMF_ADAPTER_MASK = (_dmf_adapter_lsb << CONV_OP_LIMIT) - _dmf_adapter_lsb + }; + static methodOop decode_method(oop x, klassOop& receiver_limit_result, int& decode_flags_result); + enum { + // format of query to getConstant: + GC_JVM_PUSH_LIMIT = 0, + GC_JVM_STACK_MOVE_LIMIT = 1, + + // format of result from getTarget / encode_target: + ETF_HANDLE_OR_METHOD_NAME = 0, // all available data (immediate MH or method) + ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self) + ETF_METHOD_NAME = 2, // ultimate method as MemberName + ETF_REFLECT_METHOD = 3 // ultimate method as java.lang.reflect object (sans refClass) + }; + static int get_named_constant(int which, Handle name_box, TRAPS); + static oop encode_target(Handle mh, int format, TRAPS); // report vmtarget (to Java code) + static bool class_cast_needed(klassOop src, klassOop dst); + + private: + // These checkers operate on a pair of whole MethodTypes: + static const char* check_method_type_change(oop src_mtype, int src_beg, int src_end, + int insert_argnum, oop insert_type, + int change_argnum, oop change_type, + int delete_argnum, + oop dst_mtype, int dst_beg, int dst_end); + static const char* check_method_type_insertion(oop src_mtype, + int insert_argnum, oop insert_type, + oop dst_mtype) { + oop no_ref = NULL; + return check_method_type_change(src_mtype, 0, -1, + insert_argnum, insert_type, + -1, no_ref, -1, dst_mtype, 0, -1); + } + static const char* check_method_type_conversion(oop src_mtype, + int change_argnum, oop change_type, + oop dst_mtype) { + oop no_ref = NULL; + return check_method_type_change(src_mtype, 0, -1, -1, no_ref, + change_argnum, change_type, + -1, dst_mtype, 0, -1); + } + static const char* check_method_type_passthrough(oop src_mtype, oop dst_mtype) { + oop no_ref = NULL; + return check_method_type_change(src_mtype, 0, -1, + -1, no_ref, -1, no_ref, -1, + dst_mtype, 0, -1); + } + + // These checkers operate on pairs of argument or return types: + static const char* check_argument_type_change(BasicType src_type, klassOop src_klass, + BasicType dst_type, klassOop dst_klass, + int argnum); + + static const char* check_argument_type_change(oop src_type, oop dst_type, + int argnum) { + klassOop src_klass = NULL, dst_klass = NULL; + BasicType src_bt = java_lang_Class::as_BasicType(src_type, &src_klass); + BasicType dst_bt = java_lang_Class::as_BasicType(dst_type, &dst_klass); + return check_argument_type_change(src_bt, src_klass, + dst_bt, dst_klass, argnum); + } + + static const char* check_return_type_change(oop src_type, oop dst_type) { + return check_argument_type_change(src_type, dst_type, -1); + } + + static const char* check_return_type_change(BasicType src_type, klassOop src_klass, + BasicType dst_type, klassOop dst_klass) { + return check_argument_type_change(src_type, src_klass, dst_type, dst_klass, -1); + } + + static const char* check_method_receiver(methodOop m, klassOop passed_recv_type); + + // These verifiers can block, and will throw an error if the checking fails: + static void verify_vmslots(Handle mh, TRAPS); + static void verify_vmargslot(Handle mh, int argnum, int argslot, TRAPS); + + static void verify_method_type(methodHandle m, Handle mtype, + bool has_bound_oop, + KlassHandle bound_oop_type, + TRAPS); + + static void verify_method_signature(methodHandle m, Handle mtype, + int first_ptype_pos, + KlassHandle insert_ptype, TRAPS); + + static void verify_DirectMethodHandle(Handle mh, methodHandle m, TRAPS); + static void verify_BoundMethodHandle(Handle mh, Handle target, int argnum, + bool direct_to_method, TRAPS); + static void verify_BoundMethodHandle_with_receiver(Handle mh, methodHandle m, TRAPS); + static void verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS); + + public: + + // Fill in the fields of a DirectMethodHandle mh. (MH.type must be pre-filled.) + static void init_DirectMethodHandle(Handle mh, methodHandle method, bool do_dispatch, TRAPS); + + // Fill in the fields of a BoundMethodHandle mh. (MH.type, BMH.argument must be pre-filled.) + static void init_BoundMethodHandle(Handle mh, Handle target, int argnum, TRAPS); + static void init_BoundMethodHandle_with_receiver(Handle mh, + methodHandle original_m, + KlassHandle receiver_limit, + int decode_flags, + TRAPS); + + // Fill in the fields of an AdapterMethodHandle mh. (MH.type must be pre-filled.) + static void init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS); + +#ifdef ASSERT + static bool spot_check_entry_names(); +#endif + + private: + static methodHandle dispatch_decoded_method(methodHandle m, + KlassHandle receiver_limit, + int decode_flags, + KlassHandle receiver_klass, + TRAPS); + + static bool same_basic_type_for_arguments(BasicType src, BasicType dst, + bool for_return = false); + static bool same_basic_type_for_returns(BasicType src, BasicType dst) { + return same_basic_type_for_arguments(src, dst, true); + } + + enum { // arg_mask values + _INSERT_NO_MASK = -1, + _INSERT_REF_MASK = 0, + _INSERT_INT_MASK = 1, + _INSERT_LONG_MASK = 3 + }; + static void insert_arg_slots(MacroAssembler* _masm, + RegisterOrConstant arg_slots, + int arg_mask, + Register rax_argslot, + Register rbx_temp, Register rdx_temp); + + static void remove_arg_slots(MacroAssembler* _masm, + RegisterOrConstant arg_slots, + Register rax_argslot, + Register rbx_temp, Register rdx_temp); +}; + + +// Access methods for the "entry" field of a java.dyn.MethodHandle. +// The field is primarily a jump target for compiled calls. +// However, we squirrel away some nice pointers for other uses, +// just before the jump target. +// Aspects of a method handle entry: +// - from_compiled_entry - stub used when compiled code calls the MH +// - from_interpreted_entry - stub used when the interpreter calls the MH +// - type_checking_entry - stub for runtime casting between MHForm siblings (NYI) +class MethodHandleEntry { + public: + class Data { + friend class MethodHandleEntry; + size_t _total_size; // size including Data and code stub + MethodHandleEntry* _type_checking_entry; + address _from_interpreted_entry; + MethodHandleEntry* method_entry() { return (MethodHandleEntry*)(this + 1); } + }; + + Data* data() { return (Data*)this - 1; } + + address start_address() { return (address) data(); } + address end_address() { return start_address() + data()->_total_size; } + + address from_compiled_entry() { return (address) this; } + + address from_interpreted_entry() { return data()->_from_interpreted_entry; } + void set_from_interpreted_entry(address e) { data()->_from_interpreted_entry = e; } + + MethodHandleEntry* type_checking_entry() { return data()->_type_checking_entry; } + void set_type_checking_entry(MethodHandleEntry* e) { data()->_type_checking_entry = e; } + + void set_end_address(address end_addr) { + size_t total_size = end_addr - start_address(); + assert(total_size > 0 && total_size < 0x1000, "reasonable end address"); + data()->_total_size = total_size; + } + + // Compiler support: + static int from_interpreted_entry_offset_in_bytes() { + return (int)( offset_of(Data, _from_interpreted_entry) - sizeof(Data) ); + } + static int type_checking_entry_offset_in_bytes() { + return (int)( offset_of(Data, _from_interpreted_entry) - sizeof(Data) ); + } + + static address start_compiled_entry(MacroAssembler* _masm, + address interpreted_entry = NULL); + static MethodHandleEntry* finish_compiled_entry(MacroAssembler* masm, address start_addr); +}; + +address MethodHandles::from_compiled_entry(EntryKind ek) { return entry(ek)->from_compiled_entry(); } +address MethodHandles::from_interpreted_entry(EntryKind ek) { return entry(ek)->from_interpreted_entry(); } diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/prims/nativeLookup.cpp --- a/src/share/vm/prims/nativeLookup.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/prims/nativeLookup.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,6 +78,7 @@ extern "C" { void JNICALL JVM_RegisterUnsafeMethods(JNIEnv *env, jclass unsafecls); + void JNICALL JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass unsafecls); void JNICALL JVM_RegisterPerfMethods(JNIEnv *env, jclass perfclass); } @@ -97,6 +98,9 @@ if (strstr(jni_name, "Java_sun_misc_Unsafe_registerNatives") != NULL) { return CAST_FROM_FN_PTR(address, JVM_RegisterUnsafeMethods); } + if (strstr(jni_name, "Java_sun_dyn_MethodHandleNatives_registerNatives") != NULL) { + return CAST_FROM_FN_PTR(address, JVM_RegisterMethodHandleMethods); + } if (strstr(jni_name, "Java_sun_misc_Perf_registerNatives") != NULL) { return CAST_FROM_FN_PTR(address, JVM_RegisterPerfMethods); } diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/runtime/arguments.cpp --- a/src/share/vm/runtime/arguments.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/runtime/arguments.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -2619,6 +2619,13 @@ } #endif // PRODUCT + if (EnableMethodHandles && !AnonymousClasses) { + if (!FLAG_IS_DEFAULT(AnonymousClasses)) { + warning("forcing AnonymousClasses true to enable EnableMethodHandles"); + } + AnonymousClasses = true; + } + if (PrintGCDetails) { // Turn on -verbose:gc options as well PrintGC = true; diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/runtime/globals.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -3298,6 +3298,21 @@ product(bool, AnonymousClasses, false, \ "support sun.misc.Unsafe.defineAnonymousClass") \ \ + product(bool, EnableMethodHandles, false, \ + "support method handles (true by default under JSR 292)") \ + \ + diagnostic(intx, MethodHandlePushLimit, 3, \ + "number of additional stack slots a method handle may push") \ + \ + develop(bool, TraceMethodHandles, false, \ + "trace internal method handle operations") \ + \ + diagnostic(bool, VerifyMethodHandles, trueInDebug, \ + "perform extra checks when constructing method handles") \ + \ + diagnostic(bool, OptimizeMethodHandles, true, \ + "when constructing method handles, try to improve them") \ + \ product(bool, TaggedStackInterpreter, false, \ "Insert tags in interpreter execution stack for oopmap generaion")\ \ diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/runtime/sharedRuntime.cpp --- a/src/share/vm/runtime/sharedRuntime.cpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/runtime/sharedRuntime.cpp Wed Apr 08 10:56:49 2009 -0700 @@ -1471,9 +1471,73 @@ return generate_class_cast_message(objName, targetKlass->external_name()); } +char* SharedRuntime::generate_wrong_method_type_message(JavaThread* thread, + oopDesc* required, + oopDesc* actual) { + assert(EnableMethodHandles, ""); + oop singleKlass = wrong_method_type_is_for_single_argument(thread, required); + if (singleKlass != NULL) { + const char* objName = "argument or return value"; + if (actual != NULL) { + // be flexible about the junk passed in: + klassOop ak = (actual->is_klass() + ? (klassOop)actual + : actual->klass()); + objName = Klass::cast(ak)->external_name(); + } + Klass* targetKlass = Klass::cast(required->is_klass() + ? (klassOop)required + : java_lang_Class::as_klassOop(required)); + return generate_class_cast_message(objName, targetKlass->external_name()); + } else { + // %%% need to get the MethodType string, without messing around too much + // Get a signature from the invoke instruction + const char* mhName = "method handle"; + const char* targetType = "the required signature"; + vframeStream vfst(thread, true); + if (!vfst.at_end()) { + Bytecode_invoke* call = Bytecode_invoke_at(vfst.method(), vfst.bci()); + methodHandle target; + { + EXCEPTION_MARK; + target = call->static_target(THREAD); + if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; } + } + if (target.not_null() + && target->is_method_handle_invoke() + && required == target->method_handle_type()) { + targetType = target->signature()->as_C_string(); + } + } + klassOop kignore; int fignore; + methodOop actual_method = MethodHandles::decode_method(actual, + kignore, fignore); + if (actual_method != NULL) { + if (actual_method->name() == vmSymbols::invoke_name()) + mhName = "$"; + else + mhName = actual_method->signature()->as_C_string(); + if (mhName[0] == '$') + mhName = actual_method->signature()->as_C_string(); + } + return generate_class_cast_message(mhName, targetType, + " cannot be called as "); + } +} + +oop SharedRuntime::wrong_method_type_is_for_single_argument(JavaThread* thr, + oopDesc* required) { + if (required == NULL) return NULL; + if (required->klass() == SystemDictionary::class_klass()) + return required; + if (required->is_klass()) + return Klass::cast(klassOop(required))->java_mirror(); + return NULL; +} + + char* SharedRuntime::generate_class_cast_message( - const char* objName, const char* targetKlassName) { - const char* desc = " cannot be cast to "; + const char* objName, const char* targetKlassName, const char* desc) { size_t msglen = strlen(objName) + strlen(desc) + strlen(targetKlassName) + 1; char* message = NEW_RESOURCE_ARRAY(char, msglen); diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/runtime/sharedRuntime.hpp --- a/src/share/vm/runtime/sharedRuntime.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/runtime/sharedRuntime.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -212,10 +212,32 @@ static char* generate_class_cast_message(JavaThread* thr, const char* name); /** + * Fill in the message for a WrongMethodTypeException + * + * @param thr the current thread + * @param mtype (optional) expected method type (or argument class) + * @param mhandle (optional) actual method handle (or argument) + * @return the dynamically allocated exception message + * + * BCP for the frame on top of the stack must refer to an + * 'invokevirtual' op for a method handle, or an 'invokedyamic' op. + * The caller (or one of its callers) must use a ResourceMark + * in order to correctly free the result. + */ + static char* generate_wrong_method_type_message(JavaThread* thr, + oopDesc* mtype = NULL, + oopDesc* mhandle = NULL); + + /** Return non-null if the mtype is a klass or Class, not a MethodType. */ + static oop wrong_method_type_is_for_single_argument(JavaThread* thr, + oopDesc* mtype); + + /** * Fill in the "X cannot be cast to a Y" message for ClassCastException * * @param name the name of the class of the object attempted to be cast * @param klass the name of the target klass attempt + * @param gripe the specific kind of problem being reported * @return the dynamically allocated exception message (must be freed * by the caller using a resource mark) * @@ -224,7 +246,8 @@ * The caller (or one of it's callers) must use a ResourceMark * in order to correctly free the result. */ - static char* generate_class_cast_message(const char* name, const char* klass); + static char* generate_class_cast_message(const char* name, const char* klass, + const char* gripe = " cannot be cast to "); // Resolves a call site- may patch in the destination of the call into the // compiled code. diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/utilities/accessFlags.hpp --- a/src/share/vm/utilities/accessFlags.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/utilities/accessFlags.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,6 +47,8 @@ JVM_ACC_IS_OLD = 0x00010000, // RedefineClasses() has replaced this method JVM_ACC_IS_OBSOLETE = 0x00020000, // RedefineClasses() has made method obsolete JVM_ACC_IS_PREFIXED_NATIVE = 0x00040000, // JVMTI has prefixed this native method + JVM_MH_INVOKE_BITS // = 0x10001100 // MethodHandle.invoke quasi-native + = (JVM_ACC_NATIVE | JVM_ACC_SYNTHETIC | JVM_ACC_MONITOR_MATCH), // klassOop flags JVM_ACC_HAS_MIRANDA_METHODS = 0x10000000, // True if this class has miranda methods in it's vtable @@ -72,6 +74,7 @@ // flags accepted by set_field_flags() JVM_ACC_FIELD_FLAGS = 0x00008000 | JVM_ACC_WRITTEN_FLAGS + }; @@ -114,6 +117,15 @@ bool is_obsolete () const { return (_flags & JVM_ACC_IS_OBSOLETE ) != 0; } bool is_prefixed_native () const { return (_flags & JVM_ACC_IS_PREFIXED_NATIVE ) != 0; } + // JSR 292: A method of the form MethodHandle.invoke(A...)R method is + // neither bytecoded nor a JNI native, but rather a fast call through + // a lightweight method handle object. Because it is not bytecoded, + // it has the native bit set, but the monitor-match bit is also set + // to distinguish it from a JNI native (which never has the match bit set). + // The synthetic bit is also present, because such a method is never + // explicitly defined in Java code. + bool is_method_handle_invoke () const { return (_flags & JVM_MH_INVOKE_BITS) == JVM_MH_INVOKE_BITS; } + // klassOop flags bool has_miranda_methods () const { return (_flags & JVM_ACC_HAS_MIRANDA_METHODS ) != 0; } bool has_vanilla_constructor () const { return (_flags & JVM_ACC_HAS_VANILLA_CONSTRUCTOR) != 0; } @@ -199,6 +211,14 @@ jshort as_short() { return (jshort)_flags; } jint as_int() { return _flags; } + inline friend AccessFlags accessFlags_from(jint flags); + // Printing/debugging void print_on(outputStream* st) const PRODUCT_RETURN; }; + +inline AccessFlags accessFlags_from(jint flags) { + AccessFlags af; + af._flags = flags; + return af; +} diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/utilities/exceptions.hpp --- a/src/share/vm/utilities/exceptions.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/utilities/exceptions.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -237,6 +237,9 @@ #define THROW_ARG_0(name, signature, arg) THROW_ARG_(name, signature, arg, 0) #define THROW_MSG_CAUSE_0(name, message, cause) THROW_MSG_CAUSE_(name, message, cause, 0) +#define THROW_NULL(name) THROW_(name, NULL) +#define THROW_MSG_NULL(name, message) THROW_MSG_(name, message, NULL) + // The CATCH macro checks that no exception has been thrown by a function; it is used at // call sites about which is statically known that the callee cannot throw an exception // even though it is declared with TRAPS. diff -r 1d037ecd7960 -r e5b0439ef4ae src/share/vm/utilities/globalDefinitions.hpp --- a/src/share/vm/utilities/globalDefinitions.hpp Wed Apr 08 00:12:59 2009 -0700 +++ b/src/share/vm/utilities/globalDefinitions.hpp Wed Apr 08 10:56:49 2009 -0700 @@ -408,6 +408,15 @@ return T_BOOLEAN <= t && t <= T_LONG; } +inline bool is_subword_type(BasicType t) { + // these guys are processed exactly like T_INT in calling sequences: + return (t == T_BOOLEAN || t == T_CHAR || t == T_BYTE || t == T_SHORT); +} + +inline bool is_signed_subword_type(BasicType t) { + return (t == T_BYTE || t == T_SHORT); +} + // Convert a char from a classfile signature to a BasicType inline BasicType char2type(char c) { switch( c ) {