--- a/.hgtags Tue May 24 14:15:14 2011 -0700
+++ b/.hgtags Tue May 24 14:32:10 2011 -0700
@@ -116,3 +116,4 @@
955488f34ca418f6cdab843d61c20d2c615637d9 jdk7-b139
f4298bc3f4b6baa315643be06966f09684290068 jdk7-b140
5d86d0c7692e8f4a58d430d68c03594e2d3403b3 jdk7-b141
+92bf0655022d4187e9b49c1400f98fb3392a4630 jdk7-b142
--- a/.hgtags-top-repo Tue May 24 14:15:14 2011 -0700
+++ b/.hgtags-top-repo Tue May 24 14:32:10 2011 -0700
@@ -116,3 +116,4 @@
7ed6d0b9aaa12320832a7ddadb88d6d8d0dda4c1 jdk7-b139
dcfe74f1c6553c556e7d361c30b0b614eb5e40f6 jdk7-b140
c6569c5585851dfd39b8de8e021c3c312f51af12 jdk7-b141
+cfbbdb77eac0397b03eb99ee2e07ea00e0a7b81e jdk7-b142
--- a/Makefile Tue May 24 14:15:14 2011 -0700
+++ b/Makefile Tue May 24 14:32:10 2011 -0700
@@ -25,6 +25,34 @@
BUILD_PARENT_DIRECTORY=.
+# Basename of any originally supplied ALT_OUTPUTDIR directory
+ifndef ORIG_OUTPUTDIR_BASENAME
+ ifdef ALT_OUTPUTDIR
+ ORIG_OUTPUTDIR_BASENAME := $(shell basename $(ALT_OUTPUTDIR))
+ else
+ ORIG_OUTPUTDIR_BASENAME = $(PLATFORM)-$(ARCH)
+ endif
+endif
+export ORIG_OUTPUTDIR_BASENAME
+
+# The three possible directories created for output (3 build flavors)
+OUTPUTDIR_BASENAME- = $(ORIG_OUTPUTDIR_BASENAME)
+OUTPUTDIR_BASENAME-debug = $(ORIG_OUTPUTDIR_BASENAME)-debug
+OUTPUTDIR_BASENAME-fastdebug = $(ORIG_OUTPUTDIR_BASENAME)-fastdebug
+
+# Relative path to a debug output area
+REL_JDK_OUTPUTDIR = ../$(OUTPUTDIR_BASENAME-$(DEBUG_NAME))
+
+# The created jdk image directory
+JDK_IMAGE_DIRNAME = j2sdk-image
+JDK_IMAGE_DIR = $(OUTPUTDIR)/$(JDK_IMAGE_DIRNAME)
+ABS_JDK_IMAGE_DIR = $(ABS_OUTPUTDIR)/$(JDK_IMAGE_DIRNAME)
+
+# Relative path from an output directory to the image directory
+REL_JDK_IMAGE_DIR = ../$(OUTPUTDIR_BASENAME-$(DEBUG_NAME))/$(JDK_IMAGE_DIRNAME)
+REL_JDK_DEBUG_IMAGE_DIR = ../$(OUTPUTDIR_BASENAME-debug)/$(JDK_IMAGE_DIRNAME)
+REL_JDK_FASTDEBUG_IMAGE_DIR = ../$(OUTPUTDIR_BASENAME-fastdebug)/$(JDK_IMAGE_DIRNAME)
+
ifndef TOPDIR
TOPDIR:=.
endif
@@ -98,8 +126,7 @@
# Generic build of basic repo series
generic_build_repo_series:: $(SOURCE_TIPS)
- $(MKDIR) -p $(OUTPUTDIR)
- $(MKDIR) -p $(OUTPUTDIR)/j2sdk-image
+ $(MKDIR) -p $(JDK_IMAGE_DIR)
@$(call StartTimer)
ifeq ($(BUILD_LANGTOOLS), true)
@@ -146,8 +173,8 @@
#
# DEBUG_NAME is fastdebug or debug
# ALT_OUTPUTDIR is changed to have -debug or -fastdebug suffix
-# The resulting j2sdk-image is used by the install makefiles to create a
-# debug install bundle jdk-*-debug-** bundle (tar or zip)
+# The resulting image directory (j2sdk-image) is used by the install makefiles
+# to create a debug install bundle jdk-*-debug-** bundle (tar or zip)
# which will install in the debug or fastdebug subdirectory of the
# normal product install area.
# The install process needs to know what the DEBUG_NAME is, so
@@ -160,8 +187,8 @@
# Location of fresh bootdir output
ABS_BOOTDIR_OUTPUTDIR=$(ABS_OUTPUTDIR)/bootjdk
-FRESH_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/j2sdk-image
-FRESH_DEBUG_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-$(DEBUG_NAME)/j2sdk-image
+FRESH_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/$(JDK_IMAGE_DIRNAME)
+FRESH_DEBUG_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/$(REL_JDK_IMAGE_DIR)
create_fresh_product_bootdir: FRC
$(MAKE) ALT_OUTPUTDIR=$(ABS_BOOTDIR_OUTPUTDIR) \
@@ -226,7 +253,7 @@
generic_debug_build:
$(MAKE) \
- ALT_OUTPUTDIR=$(ABS_OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-$(DEBUG_NAME) \
+ ALT_OUTPUTDIR=$(ABS_OUTPUTDIR)/$(REL_JDK_OUTPUTDIR) \
DEBUG_NAME=$(DEBUG_NAME) \
GENERATE_DOCS=false \
$(BOOT_CYCLE_DEBUG_SETTINGS) \
@@ -254,8 +281,6 @@
clobber:: REPORT_BUILD_TIMES=
clobber::
$(RM) -r $(OUTPUTDIR)/*
- $(RM) -r $(OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-debug/*
- $(RM) -r $(OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-fastdebug/*
-($(RMDIR) -p $(OUTPUTDIR) > $(DEV_NULL) 2>&1; $(TRUE))
clean: clobber
@@ -489,8 +514,8 @@
# Get log file of all tests run
JDK_TO_TEST := $(shell \
- if [ -d "$(ABS_OUTPUTDIR)/j2sdk-image" ] ; then \
- $(ECHO) "$(ABS_OUTPUTDIR)/j2sdk-image"; \
+ if [ -d "$(ABS_JDK_IMAGE_DIR)" ] ; then \
+ $(ECHO) "$(ABS_JDK_IMAGE_DIR)"; \
elif [ -d "$(ABS_OUTPUTDIR)/bin" ] ; then \
$(ECHO) "$(ABS_OUTPUTDIR)"; \
elif [ "$(PRODUCT_HOME)" != "" -a -d "$(PRODUCT_HOME)/bin" ] ; then \
--- a/corba/.hgtags Tue May 24 14:15:14 2011 -0700
+++ b/corba/.hgtags Tue May 24 14:32:10 2011 -0700
@@ -116,3 +116,4 @@
60b074ec6fcf5cdf9efce22fdfb02326ed8fa2d3 jdk7-b139
cdf5d19ec142424489549025e9c42e51f32cf688 jdk7-b140
a58635cdd921bafef353f4864184a0481353197b jdk7-b141
+a2f340a048c88d10cbedc0504f5cf03d39925a40 jdk7-b142
--- a/hotspot/.hgtags Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/.hgtags Tue May 24 14:32:10 2011 -0700
@@ -170,3 +170,5 @@
d283b82966712b353fa307845a1316da42a355f4 hs21-b10
5d07913abd59261c77f24cc04a759cb75d804099 jdk7-b141
3aea9e9feb073f5500e031be6186666bcae89aa2 hs21-b11
+9ad1548c6b63d596c411afc35147ffd5254426d9 jdk7-b142
+9ad1548c6b63d596c411afc35147ffd5254426d9 hs21-b12
--- a/hotspot/make/hotspot_version Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/make/hotspot_version Tue May 24 14:32:10 2011 -0700
@@ -35,7 +35,7 @@
HS_MAJOR_VER=21
HS_MINOR_VER=0
-HS_BUILD_NUMBER=12
+HS_BUILD_NUMBER=13
JDK_MAJOR_VER=1
JDK_MINOR_VER=7
--- a/hotspot/make/solaris/makefiles/saproc.make Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/make/solaris/makefiles/saproc.make Tue May 24 14:32:10 2011 -0700
@@ -57,24 +57,27 @@
endif
# The libproc Pstack_iter() interface changed in Nevada-B159.
-# This logic needs to match
+# Use 'uname -r -v' to determine the Solaris version as per
+# Solaris Nevada team request. This logic needs to match:
# agent/src/os/solaris/proc/saproc.cpp: set_has_newer_Pstack_iter():
# - skip SunOS 4 or older
# - skip Solaris 10 or older
-# - skip two digit Nevada builds
-# - skip three digit Nevada builds thru 149
-# - skip Nevada builds 150-158
+# - skip two digit internal Nevada builds
+# - skip three digit internal Nevada builds thru 149
+# - skip internal Nevada builds 150-158
+# - if not skipped, print define for Nevada-B159 or later
SOLARIS_11_B159_OR_LATER := \
$(shell uname -r -v \
- | sed -n ' \
- /^[0-3]\. /b \
- /^5\.[0-9] /b \
- /^5\.10 /b \
- / snv_[0-9][0-9]$/b \
- / snv_[01][0-4][0-9]$/b \
- / snv_15[0-8]$/b \
- s/.*/-DSOLARIS_11_B159_OR_LATER/p \
- ')
+ | sed -n \
+ -e '/^[0-4]\. /b' \
+ -e '/^5\.[0-9] /b' \
+ -e '/^5\.10 /b' \
+ -e '/ snv_[0-9][0-9]$/b' \
+ -e '/ snv_[01][0-4][0-9]$/b' \
+ -e '/ snv_15[0-8]$/b' \
+ -e 's/.*/-DSOLARIS_11_B159_OR_LATER/' \
+ -e 'p' \
+ )
# Uncomment the following to simulate building on Nevada-B159 or later
# when actually building on Nevada-B158 or earlier:
--- a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Tue May 24 14:32:10 2011 -0700
@@ -2176,6 +2176,7 @@
int tempcount, // Number of slots on java expression stack in use
int popframe_extra_args,
int moncount, // Number of active monitors
+ int caller_actual_parameters,
int callee_param_size,
int callee_locals_size,
frame* caller,
--- a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp Tue May 24 14:32:10 2011 -0700
@@ -811,7 +811,7 @@
#ifdef ASSERT
#define DESCRIBE_FP_OFFSET(name) \
- values.describe(-1, fp() + frame::name##_offset, #name)
+ values.describe(frame_no, fp() + frame::name##_offset, #name)
void frame::describe_pd(FrameValues& values, int frame_no) {
for (int w = 0; w < frame::register_save_words; w++) {
--- a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp Tue May 24 14:32:10 2011 -0700
@@ -423,25 +423,6 @@
return true;
}
-// This method tells the deoptimizer how big an interpreted frame must be:
-int AbstractInterpreter::size_activation(methodOop method,
- int tempcount,
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- bool is_top_frame) {
- return layout_activation(method,
- tempcount,
- popframe_extra_args,
- moncount,
- callee_param_count,
- callee_locals,
- (frame*)NULL,
- (frame*)NULL,
- is_top_frame);
-}
-
void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) {
// This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in
--- a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp Tue May 24 14:32:10 2011 -0700
@@ -142,18 +142,8 @@
Register O2_form = O2_scratch;
Register O3_adapter = O3_scratch;
__ load_heap_oop(Address(O0_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, O1_scratch)), O2_form);
- // load_heap_oop(Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter);
- // deal with old JDK versions:
- __ add( Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter);
- __ cmp(O3_adapter, O2_form);
- Label sorry_no_invoke_generic;
- __ brx(Assembler::lessUnsigned, false, Assembler::pn, sorry_no_invoke_generic);
- __ delayed()->nop();
-
- __ load_heap_oop(Address(O3_adapter, 0), O3_adapter);
- __ tst(O3_adapter);
- __ brx(Assembler::zero, false, Assembler::pn, sorry_no_invoke_generic);
- __ delayed()->nop();
+ __ load_heap_oop(Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter);
+ __ verify_oop(O3_adapter);
__ st_ptr(O3_adapter, Address(O4_argbase, 1 * Interpreter::stackElementSize));
// As a trusted first argument, pass the type being called, so the adapter knows
// the actual types of the arguments and return values.
@@ -164,12 +154,6 @@
trace_method_handle(_masm, "invokeGeneric");
__ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- __ bind(sorry_no_invoke_generic); // no invokeGeneric implementation available!
- __ mov(O0_mtype, G5_method_type); // required by throw_WrongMethodType
- // mov(G3_method_handle, G3_method_handle); // already in this register
- __ jump_to(AddressLiteral(Interpreter::throw_WrongMethodType_entry()), O1_scratch);
- __ delayed()->nop();
-
return entry_point;
}
--- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Tue May 24 14:32:10 2011 -0700
@@ -1623,6 +1623,7 @@
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_local_count,
frame* caller,
@@ -1698,21 +1699,35 @@
popframe_extra_args;
int local_words = method->max_locals() * Interpreter::stackElementWords;
+ NEEDS_CLEANUP;
intptr_t* locals;
- if (caller->is_compiled_frame()) {
- // Compiled frames do not allocate a varargs area so place them
- // next to the register save area.
- locals = fp + frame::register_save_words + local_words - 1;
- // Caller wants his own SP back
- int caller_frame_size = caller->cb()->frame_size();
- *interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS;
+ if (caller->is_interpreted_frame()) {
+ // Can force the locals area to end up properly overlapping the top of the expression stack.
+ intptr_t* Lesp_ptr = caller->interpreter_frame_tos_address() - 1;
+ // Note that this computation means we replace size_of_parameters() values from the caller
+ // interpreter frame's expression stack with our argument locals
+ int parm_words = caller_actual_parameters * Interpreter::stackElementWords;
+ locals = Lesp_ptr + parm_words;
+ int delta = local_words - parm_words;
+ int computed_sp_adjustment = (delta > 0) ? round_to(delta, WordsPerLong) : 0;
+ *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + computed_sp_adjustment) - STACK_BIAS;
} else {
- assert(caller->is_interpreted_frame() || caller->is_entry_frame(), "only possible cases");
- // The entry and interpreter frames are laid out like normal C
- // frames so place the locals adjacent to the varargs area.
- locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1;
- if (caller->is_interpreted_frame()) {
- *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + rounded_cls) - STACK_BIAS;
+ assert(caller->is_compiled_frame() || caller->is_entry_frame(), "only possible cases");
+ // Don't have Lesp available; lay out locals block in the caller
+ // adjacent to the register window save area.
+ //
+ // Compiled frames do not allocate a varargs area which is why this if
+ // statement is needed.
+ //
+ if (caller->is_compiled_frame()) {
+ locals = fp + frame::register_save_words + local_words - 1;
+ } else {
+ locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1;
+ }
+ if (!caller->is_entry_frame()) {
+ // Caller wants his own SP back
+ int caller_frame_size = caller->cb()->frame_size();
+ *interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS;
}
}
if (TraceDeoptimization) {
--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp Tue May 24 14:32:10 2011 -0700
@@ -234,6 +234,20 @@
a._disp += disp;
return a;
}
+ Address plus_disp(RegisterOrConstant disp, ScaleFactor scale = times_1) const {
+ Address a = (*this);
+ a._disp += disp.constant_or_zero() * scale_size(scale);
+ if (disp.is_register()) {
+ assert(!a.index()->is_valid(), "competing indexes");
+ a._index = disp.as_register();
+ a._scale = scale;
+ }
+ return a;
+ }
+ bool is_same_address(Address a) const {
+ // disregard _rspec
+ return _base == a._base && _disp == a._disp && _index == a._index && _scale == a._scale;
+ }
// The following two overloads are used in connection with the
// ByteSize type (see sizes.hpp). They simplify the use of
@@ -2029,6 +2043,10 @@
void addptr(Register dst, Address src) { LP64_ONLY(addq(dst, src)) NOT_LP64(addl(dst, src)); }
void addptr(Register dst, int32_t src);
void addptr(Register dst, Register src);
+ void addptr(Register dst, RegisterOrConstant src) {
+ if (src.is_constant()) addptr(dst, (int) src.as_constant());
+ else addptr(dst, src.as_register());
+ }
void andptr(Register dst, int32_t src);
void andptr(Register src1, Register src2) { LP64_ONLY(andq(src1, src2)) NOT_LP64(andl(src1, src2)) ; }
@@ -2090,7 +2108,10 @@
void subptr(Register dst, Address src) { LP64_ONLY(subq(dst, src)) NOT_LP64(subl(dst, src)); }
void subptr(Register dst, int32_t src);
void subptr(Register dst, Register src);
-
+ void subptr(Register dst, RegisterOrConstant src) {
+ if (src.is_constant()) subptr(dst, (int) src.as_constant());
+ else subptr(dst, src.as_register());
+ }
void sbbptr(Address dst, int32_t src) { LP64_ONLY(sbbq(dst, src)) NOT_LP64(sbbl(dst, src)); }
void sbbptr(Register dst, int32_t src) { LP64_ONLY(sbbq(dst, src)) NOT_LP64(sbbl(dst, src)); }
@@ -2288,6 +2309,11 @@
void movptr(Address dst, Register src);
+ void movptr(Register dst, RegisterOrConstant src) {
+ if (src.is_constant()) movptr(dst, src.as_constant());
+ else movptr(dst, src.as_register());
+ }
+
#ifdef _LP64
// Generally the next two are only used for moving NULL
// Although there are situations in initializing the mark word where
--- a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp Tue May 24 14:32:10 2011 -0700
@@ -2339,14 +2339,15 @@
}
int AbstractInterpreter::layout_activation(methodOop method,
- int tempcount, //
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- frame* caller,
- frame* interpreter_frame,
- bool is_top_frame) {
+ int tempcount, //
+ int popframe_extra_args,
+ int moncount,
+ int caller_actual_parameters,
+ int callee_param_count,
+ int callee_locals,
+ frame* caller,
+ frame* interpreter_frame,
+ bool is_top_frame) {
assert(popframe_extra_args == 0, "FIX ME");
// NOTE this code must exactly mimic what InterpreterGenerator::generate_compute_interpreter_state()
--- a/hotspot/src/cpu/x86/vm/frame_x86.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/frame_x86.cpp Tue May 24 14:32:10 2011 -0700
@@ -339,7 +339,6 @@
return fr;
}
-
//------------------------------------------------------------------------------
// frame::verify_deopt_original_pc
//
@@ -361,6 +360,55 @@
}
#endif
+//------------------------------------------------------------------------------
+// frame::adjust_unextended_sp
+void frame::adjust_unextended_sp() {
+ // If we are returning to a compiled MethodHandle call site, the
+ // saved_fp will in fact be a saved value of the unextended SP. The
+ // simplest way to tell whether we are returning to such a call site
+ // is as follows:
+
+ nmethod* sender_nm = (_cb == NULL) ? NULL : _cb->as_nmethod_or_null();
+ if (sender_nm != NULL) {
+ // If the sender PC is a deoptimization point, get the original
+ // PC. For MethodHandle call site the unextended_sp is stored in
+ // saved_fp.
+ if (sender_nm->is_deopt_mh_entry(_pc)) {
+ DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, _fp));
+ _unextended_sp = _fp;
+ }
+ else if (sender_nm->is_deopt_entry(_pc)) {
+ DEBUG_ONLY(verify_deopt_original_pc(sender_nm, _unextended_sp));
+ }
+ else if (sender_nm->is_method_handle_return(_pc)) {
+ _unextended_sp = _fp;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// frame::update_map_with_saved_link
+void frame::update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr) {
+ // The interpreter and compiler(s) always save EBP/RBP in a known
+ // location on entry. We must record where that location is
+ // so this if EBP/RBP was live on callout from c2 we can find
+ // the saved copy no matter what it called.
+
+ // Since the interpreter always saves EBP/RBP if we record where it is then
+ // we don't have to always save EBP/RBP on entry and exit to c2 compiled
+ // code, on entry will be enough.
+ map->set_location(rbp->as_VMReg(), (address) link_addr);
+#ifdef AMD64
+ // this is weird "H" ought to be at a higher address however the
+ // oopMaps seems to have the "H" regs at the same address and the
+ // vanilla register.
+ // XXXX make this go away
+ if (true) {
+ map->set_location(rbp->as_VMReg()->next(), (address) link_addr);
+ }
+#endif // AMD64
+}
+
//------------------------------------------------------------------------------
// frame::sender_for_interpreter_frame
@@ -372,54 +420,13 @@
// This is the sp before any possible extension (adapter/locals).
intptr_t* unextended_sp = interpreter_frame_sender_sp();
- // Stored FP.
- intptr_t* saved_fp = link();
-
- address sender_pc = this->sender_pc();
- CodeBlob* sender_cb = CodeCache::find_blob_unsafe(sender_pc);
- assert(sender_cb, "sanity");
- nmethod* sender_nm = sender_cb->as_nmethod_or_null();
-
- if (sender_nm != NULL) {
- // If the sender PC is a deoptimization point, get the original
- // PC. For MethodHandle call site the unextended_sp is stored in
- // saved_fp.
- if (sender_nm->is_deopt_mh_entry(sender_pc)) {
- DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, saved_fp));
- unextended_sp = saved_fp;
- }
- else if (sender_nm->is_deopt_entry(sender_pc)) {
- DEBUG_ONLY(verify_deopt_original_pc(sender_nm, unextended_sp));
- }
- else if (sender_nm->is_method_handle_return(sender_pc)) {
- unextended_sp = saved_fp;
- }
- }
-
- // The interpreter and compiler(s) always save EBP/RBP in a known
- // location on entry. We must record where that location is
- // so this if EBP/RBP was live on callout from c2 we can find
- // the saved copy no matter what it called.
-
- // Since the interpreter always saves EBP/RBP if we record where it is then
- // we don't have to always save EBP/RBP on entry and exit to c2 compiled
- // code, on entry will be enough.
#ifdef COMPILER2
if (map->update_map()) {
- map->set_location(rbp->as_VMReg(), (address) addr_at(link_offset));
-#ifdef AMD64
- // this is weird "H" ought to be at a higher address however the
- // oopMaps seems to have the "H" regs at the same address and the
- // vanilla register.
- // XXXX make this go away
- if (true) {
- map->set_location(rbp->as_VMReg()->next(), (address)addr_at(link_offset));
- }
-#endif // AMD64
+ update_map_with_saved_link(map, (intptr_t**) addr_at(link_offset));
}
#endif // COMPILER2
- return frame(sender_sp, unextended_sp, saved_fp, sender_pc);
+ return frame(sender_sp, unextended_sp, link(), sender_pc());
}
@@ -427,6 +434,7 @@
// frame::sender_for_compiled_frame
frame frame::sender_for_compiled_frame(RegisterMap* map) const {
assert(map != NULL, "map must be set");
+ assert(!is_ricochet_frame(), "caller must handle this");
// frame owned by optimizing compiler
assert(_cb->frame_size() >= 0, "must have non-zero frame size");
@@ -438,31 +446,7 @@
// This is the saved value of EBP which may or may not really be an FP.
// It is only an FP if the sender is an interpreter frame (or C1?).
- intptr_t* saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset);
-
- // If we are returning to a compiled MethodHandle call site, the
- // saved_fp will in fact be a saved value of the unextended SP. The
- // simplest way to tell whether we are returning to such a call site
- // is as follows:
- CodeBlob* sender_cb = CodeCache::find_blob_unsafe(sender_pc);
- assert(sender_cb, "sanity");
- nmethod* sender_nm = sender_cb->as_nmethod_or_null();
-
- if (sender_nm != NULL) {
- // If the sender PC is a deoptimization point, get the original
- // PC. For MethodHandle call site the unextended_sp is stored in
- // saved_fp.
- if (sender_nm->is_deopt_mh_entry(sender_pc)) {
- DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, saved_fp));
- unextended_sp = saved_fp;
- }
- else if (sender_nm->is_deopt_entry(sender_pc)) {
- DEBUG_ONLY(verify_deopt_original_pc(sender_nm, unextended_sp));
- }
- else if (sender_nm->is_method_handle_return(sender_pc)) {
- unextended_sp = saved_fp;
- }
- }
+ intptr_t** saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset);
if (map->update_map()) {
// Tell GC to use argument oopmaps for some runtime stubs that need it.
@@ -472,23 +456,15 @@
if (_cb->oop_maps() != NULL) {
OopMapSet::update_register_map(this, map);
}
+
// Since the prolog does the save and restore of EBP there is no oopmap
// for it so we must fill in its location as if there was an oopmap entry
// since if our caller was compiled code there could be live jvm state in it.
- map->set_location(rbp->as_VMReg(), (address) (sender_sp - frame::sender_sp_offset));
-#ifdef AMD64
- // this is weird "H" ought to be at a higher address however the
- // oopMaps seems to have the "H" regs at the same address and the
- // vanilla register.
- // XXXX make this go away
- if (true) {
- map->set_location(rbp->as_VMReg()->next(), (address) (sender_sp - frame::sender_sp_offset));
- }
-#endif // AMD64
+ update_map_with_saved_link(map, saved_fp_addr);
}
assert(sender_sp != sp(), "must have changed");
- return frame(sender_sp, unextended_sp, saved_fp, sender_pc);
+ return frame(sender_sp, unextended_sp, *saved_fp_addr, sender_pc);
}
@@ -502,6 +478,7 @@
if (is_entry_frame()) return sender_for_entry_frame(map);
if (is_interpreted_frame()) return sender_for_interpreter_frame(map);
assert(_cb == CodeCache::find_blob(pc()),"Must be the same");
+ if (is_ricochet_frame()) return sender_for_ricochet_frame(map);
if (_cb != NULL) {
return sender_for_compiled_frame(map);
@@ -673,7 +650,7 @@
#ifdef ASSERT
#define DESCRIBE_FP_OFFSET(name) \
- values.describe(-1, fp() + frame::name##_offset, #name)
+ values.describe(frame_no, fp() + frame::name##_offset, #name)
void frame::describe_pd(FrameValues& values, int frame_no) {
if (is_interpreted_frame()) {
--- a/hotspot/src/cpu/x86/vm/frame_x86.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/frame_x86.hpp Tue May 24 14:32:10 2011 -0700
@@ -164,6 +164,7 @@
// original sp we use that convention.
intptr_t* _unextended_sp;
+ void adjust_unextended_sp();
intptr_t* ptr_at_addr(int offset) const {
return (intptr_t*) addr_at(offset);
@@ -197,6 +198,9 @@
// expression stack tos if we are nested in a java call
intptr_t* interpreter_frame_last_sp() const;
+ // helper to update a map with callee-saved RBP
+ static void update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr);
+
#ifndef CC_INTERP
// deoptimization support
void interpreter_frame_set_last_sp(intptr_t* sp);
--- a/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp Tue May 24 14:32:10 2011 -0700
@@ -62,6 +62,7 @@
_pc = pc;
assert(pc != NULL, "no pc?");
_cb = CodeCache::find_blob(pc);
+ adjust_unextended_sp();
address original_pc = nmethod::get_deopt_original_pc(this);
if (original_pc != NULL) {
--- a/hotspot/src/cpu/x86/vm/interpreter_x86.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86.hpp Tue May 24 14:32:10 2011 -0700
@@ -26,7 +26,9 @@
#define CPU_X86_VM_INTERPRETER_X86_HPP
public:
- static Address::ScaleFactor stackElementScale() { return Address::times_4; }
+ static Address::ScaleFactor stackElementScale() {
+ return NOT_LP64(Address::times_4) LP64_ONLY(Address::times_8);
+ }
// Offset from rsp (which points to the last stack element)
static int expr_offset_in_bytes(int i) { return stackElementSize * i; }
--- a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp Tue May 24 14:32:10 2011 -0700
@@ -242,26 +242,6 @@
return entry_point;
}
-
-// This method tells the deoptimizer how big an interpreted frame must be:
-int AbstractInterpreter::size_activation(methodOop method,
- int tempcount,
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- bool is_top_frame) {
- return layout_activation(method,
- tempcount,
- popframe_extra_args,
- moncount,
- callee_param_count,
- callee_locals,
- (frame*) NULL,
- (frame*) NULL,
- is_top_frame);
-}
-
void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) {
// This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in
--- a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp Tue May 24 14:32:10 2011 -0700
@@ -362,20 +362,6 @@
}
-// This method tells the deoptimizer how big an interpreted frame must be:
-int AbstractInterpreter::size_activation(methodOop method,
- int tempcount,
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- bool is_top_frame) {
- return layout_activation(method,
- tempcount, popframe_extra_args, moncount,
- callee_param_count, callee_locals,
- (frame*) NULL, (frame*) NULL, is_top_frame);
-}
-
void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) {
// This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in
--- a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp Tue May 24 14:32:10 2011 -0700
@@ -69,23 +69,475 @@
return me;
}
+// stack walking support
+
+frame MethodHandles::ricochet_frame_sender(const frame& fr, RegisterMap *map) {
+ RicochetFrame* f = RicochetFrame::from_frame(fr);
+ if (map->update_map())
+ frame::update_map_with_saved_link(map, &f->_sender_link);
+ return frame(f->extended_sender_sp(), f->exact_sender_sp(), f->sender_link(), f->sender_pc());
+}
+
+void MethodHandles::ricochet_frame_oops_do(const frame& fr, OopClosure* blk, const RegisterMap* reg_map) {
+ RicochetFrame* f = RicochetFrame::from_frame(fr);
+
+ // pick up the argument type descriptor:
+ Thread* thread = Thread::current();
+ Handle cookie(thread, f->compute_saved_args_layout(true, true));
+
+ // process fixed part
+ blk->do_oop((oop*)f->saved_target_addr());
+ blk->do_oop((oop*)f->saved_args_layout_addr());
+
+ // process variable arguments:
+ if (cookie.is_null()) return; // no arguments to describe
+
+ // the cookie is actually the invokeExact method for my target
+ // his argument signature is what I'm interested in
+ assert(cookie->is_method(), "");
+ methodHandle invoker(thread, methodOop(cookie()));
+ assert(invoker->name() == vmSymbols::invokeExact_name(), "must be this kind of method");
+ assert(!invoker->is_static(), "must have MH argument");
+ int slot_count = invoker->size_of_parameters();
+ assert(slot_count >= 1, "must include 'this'");
+ intptr_t* base = f->saved_args_base();
+ intptr_t* retval = NULL;
+ if (f->has_return_value_slot())
+ retval = f->return_value_slot_addr();
+ int slot_num = slot_count;
+ intptr_t* loc = &base[slot_num -= 1];
+ //blk->do_oop((oop*) loc); // original target, which is irrelevant
+ int arg_num = 0;
+ for (SignatureStream ss(invoker->signature()); !ss.is_done(); ss.next()) {
+ if (ss.at_return_type()) continue;
+ BasicType ptype = ss.type();
+ if (ptype == T_ARRAY) ptype = T_OBJECT; // fold all refs to T_OBJECT
+ assert(ptype >= T_BOOLEAN && ptype <= T_OBJECT, "not array or void");
+ loc = &base[slot_num -= type2size[ptype]];
+ bool is_oop = (ptype == T_OBJECT && loc != retval);
+ if (is_oop) blk->do_oop((oop*)loc);
+ arg_num += 1;
+ }
+ assert(slot_num == 0, "must have processed all the arguments");
+}
+
+oop MethodHandles::RicochetFrame::compute_saved_args_layout(bool read_cache, bool write_cache) {
+ oop cookie = NULL;
+ if (read_cache) {
+ cookie = saved_args_layout();
+ if (cookie != NULL) return cookie;
+ }
+ oop target = saved_target();
+ oop mtype = java_lang_invoke_MethodHandle::type(target);
+ oop mtform = java_lang_invoke_MethodType::form(mtype);
+ cookie = java_lang_invoke_MethodTypeForm::vmlayout(mtform);
+ if (write_cache) {
+ (*saved_args_layout_addr()) = cookie;
+ }
+ return cookie;
+}
+
+void MethodHandles::RicochetFrame::generate_ricochet_blob(MacroAssembler* _masm,
+ // output params:
+ int* frame_size_in_words,
+ int* bounce_offset,
+ int* exception_offset) {
+ (*frame_size_in_words) = RicochetFrame::frame_size_in_bytes() / wordSize;
+
+ address start = __ pc();
+
#ifdef ASSERT
-static void verify_argslot(MacroAssembler* _masm, Register argslot_reg,
- const char* error_message) {
+ __ hlt(); __ hlt(); __ hlt();
+ // here's a hint of something special:
+ __ push(MAGIC_NUMBER_1);
+ __ push(MAGIC_NUMBER_2);
+#endif //ASSERT
+ __ hlt(); // not reached
+
+ // A return PC has just been popped from the stack.
+ // Return values are in registers.
+ // The ebp points into the RicochetFrame, which contains
+ // a cleanup continuation we must return to.
+
+ (*bounce_offset) = __ pc() - start;
+ BLOCK_COMMENT("ricochet_blob.bounce");
+
+ if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm);
+ trace_method_handle(_masm, "return/ricochet_blob.bounce");
+
+ __ jmp(frame_address(continuation_offset_in_bytes()));
+ __ hlt();
+ DEBUG_ONLY(__ push(MAGIC_NUMBER_2));
+
+ (*exception_offset) = __ pc() - start;
+ BLOCK_COMMENT("ricochet_blob.exception");
+
+ // compare this to Interpreter::rethrow_exception_entry, which is parallel code
+ // for example, see TemplateInterpreterGenerator::generate_throw_exception
+ // Live registers in:
+ // rax: exception
+ // rdx: return address/pc that threw exception (ignored, always equal to bounce addr)
+ __ verify_oop(rax);
+
+ // no need to empty_FPU_stack or reinit_heapbase, since caller frame will do the same if needed
+
+ // Take down the frame.
+
+ // Cf. InterpreterMacroAssembler::remove_activation.
+ leave_ricochet_frame(_masm, /*rcx_recv=*/ noreg,
+ saved_last_sp_register(),
+ /*sender_pc_reg=*/ rdx);
+
+ // In between activations - previous activation type unknown yet
+ // compute continuation point - the continuation point expects the
+ // following registers set up:
+ //
+ // rax: exception
+ // rdx: return address/pc that threw exception
+ // rsp: expression stack of caller
+ // rbp: ebp of caller
+ __ push(rax); // save exception
+ __ push(rdx); // save return address
+ Register thread_reg = LP64_ONLY(r15_thread) NOT_LP64(rdi);
+ NOT_LP64(__ get_thread(thread_reg));
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address,
+ SharedRuntime::exception_handler_for_return_address),
+ thread_reg, rdx);
+ __ mov(rbx, rax); // save exception handler
+ __ pop(rdx); // restore return address
+ __ pop(rax); // restore exception
+ __ jmp(rbx); // jump to exception
+ // handler of caller
+}
+
+void MethodHandles::RicochetFrame::enter_ricochet_frame(MacroAssembler* _masm,
+ Register rcx_recv,
+ Register rax_argv,
+ address return_handler,
+ Register rbx_temp) {
+ const Register saved_last_sp = saved_last_sp_register();
+ Address rcx_mh_vmtarget( rcx_recv, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes() );
+ Address rcx_amh_conversion( rcx_recv, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes() );
+
+ // Push the RicochetFrame a word at a time.
+ // This creates something similar to an interpreter frame.
+ // Cf. TemplateInterpreterGenerator::generate_fixed_frame.
+ BLOCK_COMMENT("push RicochetFrame {");
+ DEBUG_ONLY(int rfo = (int) sizeof(RicochetFrame));
+ assert((rfo -= wordSize) == RicochetFrame::sender_pc_offset_in_bytes(), "");
+#define RF_FIELD(push_value, name) \
+ { push_value; \
+ assert((rfo -= wordSize) == RicochetFrame::name##_offset_in_bytes(), ""); }
+ RF_FIELD(__ push(rbp), sender_link);
+ RF_FIELD(__ push(saved_last_sp), exact_sender_sp); // rsi/r13
+ RF_FIELD(__ pushptr(rcx_amh_conversion), conversion);
+ RF_FIELD(__ push(rax_argv), saved_args_base); // can be updated if args are shifted
+ RF_FIELD(__ push((int32_t) NULL_WORD), saved_args_layout); // cache for GC layout cookie
+ if (UseCompressedOops) {
+ __ load_heap_oop(rbx_temp, rcx_mh_vmtarget);
+ RF_FIELD(__ push(rbx_temp), saved_target);
+ } else {
+ RF_FIELD(__ pushptr(rcx_mh_vmtarget), saved_target);
+ }
+ __ lea(rbx_temp, ExternalAddress(return_handler));
+ RF_FIELD(__ push(rbx_temp), continuation);
+#undef RF_FIELD
+ assert(rfo == 0, "fully initialized the RicochetFrame");
+ // compute new frame pointer:
+ __ lea(rbp, Address(rsp, RicochetFrame::sender_link_offset_in_bytes()));
+ // Push guard word #1 in debug mode.
+ DEBUG_ONLY(__ push((int32_t) RicochetFrame::MAGIC_NUMBER_1));
+ // For debugging, leave behind an indication of which stub built this frame.
+ DEBUG_ONLY({ Label L; __ call(L, relocInfo::none); __ bind(L); });
+ BLOCK_COMMENT("} RicochetFrame");
+}
+
+void MethodHandles::RicochetFrame::leave_ricochet_frame(MacroAssembler* _masm,
+ Register rcx_recv,
+ Register new_sp_reg,
+ Register sender_pc_reg) {
+ assert_different_registers(rcx_recv, new_sp_reg, sender_pc_reg);
+ const Register saved_last_sp = saved_last_sp_register();
+ // Take down the frame.
+ // Cf. InterpreterMacroAssembler::remove_activation.
+ BLOCK_COMMENT("end_ricochet_frame {");
+ // TO DO: If (exact_sender_sp - extended_sender_sp) > THRESH, compact the frame down.
+ // This will keep stack in bounds even with unlimited tailcalls, each with an adapter.
+ if (rcx_recv->is_valid())
+ __ movptr(rcx_recv, RicochetFrame::frame_address(RicochetFrame::saved_target_offset_in_bytes()));
+ __ movptr(sender_pc_reg, RicochetFrame::frame_address(RicochetFrame::sender_pc_offset_in_bytes()));
+ __ movptr(saved_last_sp, RicochetFrame::frame_address(RicochetFrame::exact_sender_sp_offset_in_bytes()));
+ __ movptr(rbp, RicochetFrame::frame_address(RicochetFrame::sender_link_offset_in_bytes()));
+ __ mov(rsp, new_sp_reg);
+ BLOCK_COMMENT("} end_ricochet_frame");
+}
+
+// Emit code to verify that RBP is pointing at a valid ricochet frame.
+#ifdef ASSERT
+enum {
+ ARG_LIMIT = 255, SLOP = 4,
+ // use this parameter for checking for garbage stack movements:
+ UNREASONABLE_STACK_MOVE = (ARG_LIMIT + SLOP)
+ // the slop defends against false alarms due to fencepost errors
+};
+
+void MethodHandles::RicochetFrame::verify_clean(MacroAssembler* _masm) {
+ // The stack should look like this:
+ // ... keep1 | dest=42 | keep2 | RF | magic | handler | magic | recursive args |
+ // Check various invariants.
+ verify_offsets();
+
+ Register rdi_temp = rdi;
+ Register rcx_temp = rcx;
+ { __ push(rdi_temp); __ push(rcx_temp); }
+#define UNPUSH_TEMPS \
+ { __ pop(rcx_temp); __ pop(rdi_temp); }
+
+ Address magic_number_1_addr = RicochetFrame::frame_address(RicochetFrame::magic_number_1_offset_in_bytes());
+ Address magic_number_2_addr = RicochetFrame::frame_address(RicochetFrame::magic_number_2_offset_in_bytes());
+ Address continuation_addr = RicochetFrame::frame_address(RicochetFrame::continuation_offset_in_bytes());
+ Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes());
+ Address saved_args_base_addr = RicochetFrame::frame_address(RicochetFrame::saved_args_base_offset_in_bytes());
+
+ Label L_bad, L_ok;
+ BLOCK_COMMENT("verify_clean {");
+ // Magic numbers must check out:
+ __ cmpptr(magic_number_1_addr, (int32_t) MAGIC_NUMBER_1);
+ __ jcc(Assembler::notEqual, L_bad);
+ __ cmpptr(magic_number_2_addr, (int32_t) MAGIC_NUMBER_2);
+ __ jcc(Assembler::notEqual, L_bad);
+
+ // Arguments pointer must look reasonable:
+ __ movptr(rcx_temp, saved_args_base_addr);
+ __ cmpptr(rcx_temp, rbp);
+ __ jcc(Assembler::below, L_bad);
+ __ subptr(rcx_temp, UNREASONABLE_STACK_MOVE * Interpreter::stackElementSize);
+ __ cmpptr(rcx_temp, rbp);
+ __ jcc(Assembler::above, L_bad);
+
+ load_conversion_dest_type(_masm, rdi_temp, conversion_addr);
+ __ cmpl(rdi_temp, T_VOID);
+ __ jcc(Assembler::equal, L_ok);
+ __ movptr(rcx_temp, saved_args_base_addr);
+ load_conversion_vminfo(_masm, rdi_temp, conversion_addr);
+ __ cmpptr(Address(rcx_temp, rdi_temp, Interpreter::stackElementScale()),
+ (int32_t) RETURN_VALUE_PLACEHOLDER);
+ __ jcc(Assembler::equal, L_ok);
+ __ BIND(L_bad);
+ UNPUSH_TEMPS;
+ __ stop("damaged ricochet frame");
+ __ BIND(L_ok);
+ UNPUSH_TEMPS;
+ BLOCK_COMMENT("} verify_clean");
+
+#undef UNPUSH_TEMPS
+
+}
+#endif //ASSERT
+
+void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg) {
+ if (VerifyMethodHandles)
+ verify_klass(_masm, klass_reg, SystemDictionaryHandles::Class_klass(),
+ "AMH argument is a Class");
+ __ load_heap_oop(klass_reg, Address(klass_reg, java_lang_Class::klass_offset_in_bytes()));
+}
+
+void MethodHandles::load_conversion_vminfo(MacroAssembler* _masm, Register reg, Address conversion_field_addr) {
+ int bits = BitsPerByte;
+ int offset = (CONV_VMINFO_SHIFT / bits);
+ int shift = (CONV_VMINFO_SHIFT % bits);
+ __ load_unsigned_byte(reg, conversion_field_addr.plus_disp(offset));
+ assert(CONV_VMINFO_MASK == right_n_bits(bits - shift), "else change type of previous load");
+ assert(shift == 0, "no shift needed");
+}
+
+void MethodHandles::load_conversion_dest_type(MacroAssembler* _masm, Register reg, Address conversion_field_addr) {
+ int bits = BitsPerByte;
+ int offset = (CONV_DEST_TYPE_SHIFT / bits);
+ int shift = (CONV_DEST_TYPE_SHIFT % bits);
+ __ load_unsigned_byte(reg, conversion_field_addr.plus_disp(offset));
+ assert(CONV_TYPE_MASK == right_n_bits(bits - shift), "else change type of previous load");
+ __ shrl(reg, shift);
+ DEBUG_ONLY(int conv_type_bits = (int) exact_log2(CONV_TYPE_MASK+1));
+ assert((shift + conv_type_bits) == bits, "left justified in byte");
+}
+
+void MethodHandles::load_stack_move(MacroAssembler* _masm,
+ Register rdi_stack_move,
+ Register rcx_amh,
+ bool might_be_negative) {
+ BLOCK_COMMENT("load_stack_move");
+ Address rcx_amh_conversion(rcx_amh, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes());
+ __ movl(rdi_stack_move, rcx_amh_conversion);
+ __ sarl(rdi_stack_move, CONV_STACK_MOVE_SHIFT);
+#ifdef _LP64
+ if (might_be_negative) {
+ // clean high bits of stack motion register (was loaded as an int)
+ __ movslq(rdi_stack_move, rdi_stack_move);
+ }
+#endif //_LP64
+ if (VerifyMethodHandles) {
+ Label L_ok, L_bad;
+ int32_t stack_move_limit = 0x4000; // extra-large
+ __ cmpptr(rdi_stack_move, stack_move_limit);
+ __ jcc(Assembler::greaterEqual, L_bad);
+ __ cmpptr(rdi_stack_move, -stack_move_limit);
+ __ jcc(Assembler::greater, L_ok);
+ __ bind(L_bad);
+ __ stop("load_stack_move of garbage value");
+ __ BIND(L_ok);
+ }
+}
+
+#ifndef PRODUCT
+void MethodHandles::RicochetFrame::verify_offsets() {
+ // Check compatibility of this struct with the more generally used offsets of class frame:
+ int ebp_off = sender_link_offset_in_bytes(); // offset from struct base to local rbp value
+ assert(ebp_off + wordSize*frame::interpreter_frame_method_offset == saved_args_base_offset_in_bytes(), "");
+ assert(ebp_off + wordSize*frame::interpreter_frame_last_sp_offset == conversion_offset_in_bytes(), "");
+ assert(ebp_off + wordSize*frame::interpreter_frame_sender_sp_offset == exact_sender_sp_offset_in_bytes(), "");
+ // These last two have to be exact:
+ assert(ebp_off + wordSize*frame::link_offset == sender_link_offset_in_bytes(), "");
+ assert(ebp_off + wordSize*frame::return_addr_offset == sender_pc_offset_in_bytes(), "");
+}
+
+void MethodHandles::RicochetFrame::verify() const {
+ verify_offsets();
+ assert(magic_number_1() == MAGIC_NUMBER_1, "");
+ assert(magic_number_2() == MAGIC_NUMBER_2, "");
+ if (!Universe::heap()->is_gc_active()) {
+ if (saved_args_layout() != NULL) {
+ assert(saved_args_layout()->is_method(), "must be valid oop");
+ }
+ if (saved_target() != NULL) {
+ assert(java_lang_invoke_MethodHandle::is_instance(saved_target()), "checking frame value");
+ }
+ }
+ int conv_op = adapter_conversion_op(conversion());
+ assert(conv_op == java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS ||
+ conv_op == java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS ||
+ conv_op == java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF,
+ "must be a sane conversion");
+ if (has_return_value_slot()) {
+ assert(*return_value_slot_addr() == RETURN_VALUE_PLACEHOLDER, "");
+ }
+}
+#endif //PRODUCT
+
+#ifdef ASSERT
+void MethodHandles::verify_argslot(MacroAssembler* _masm,
+ Register argslot_reg,
+ const char* error_message) {
// Verify that argslot lies within (rsp, rbp].
Label L_ok, L_bad;
- BLOCK_COMMENT("{ verify_argslot");
+ BLOCK_COMMENT("verify_argslot {");
__ cmpptr(argslot_reg, rbp);
__ jccb(Assembler::above, L_bad);
__ cmpptr(rsp, argslot_reg);
__ jccb(Assembler::below, L_ok);
__ bind(L_bad);
__ stop(error_message);
- __ bind(L_ok);
+ __ BIND(L_ok);
BLOCK_COMMENT("} verify_argslot");
}
-#endif
+
+void MethodHandles::verify_argslots(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ Register arg_slot_base_reg,
+ bool negate_argslots,
+ const char* error_message) {
+ // Verify that [argslot..argslot+size) lies within (rsp, rbp).
+ Label L_ok, L_bad;
+ Register rdi_temp = rdi;
+ BLOCK_COMMENT("verify_argslots {");
+ __ push(rdi_temp);
+ if (negate_argslots) {
+ if (arg_slots.is_constant()) {
+ arg_slots = -1 * arg_slots.as_constant();
+ } else {
+ __ movptr(rdi_temp, arg_slots);
+ __ negptr(rdi_temp);
+ arg_slots = rdi_temp;
+ }
+ }
+ __ lea(rdi_temp, Address(arg_slot_base_reg, arg_slots, Interpreter::stackElementScale()));
+ __ cmpptr(rdi_temp, rbp);
+ __ pop(rdi_temp);
+ __ jcc(Assembler::above, L_bad);
+ __ cmpptr(rsp, arg_slot_base_reg);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_bad);
+ __ stop(error_message);
+ __ BIND(L_ok);
+ BLOCK_COMMENT("} verify_argslots");
+}
+// Make sure that arg_slots has the same sign as the given direction.
+// If (and only if) arg_slots is a assembly-time constant, also allow it to be zero.
+void MethodHandles::verify_stack_move(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots, int direction) {
+ bool allow_zero = arg_slots.is_constant();
+ if (direction == 0) { direction = +1; allow_zero = true; }
+ assert(stack_move_unit() == -1, "else add extra checks here");
+ if (arg_slots.is_register()) {
+ Label L_ok, L_bad;
+ BLOCK_COMMENT("verify_stack_move {");
+ // testl(arg_slots.as_register(), -stack_move_unit() - 1); // no need
+ // jcc(Assembler::notZero, L_bad);
+ __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD);
+ if (direction > 0) {
+ __ jcc(allow_zero ? Assembler::less : Assembler::lessEqual, L_bad);
+ __ cmpptr(arg_slots.as_register(), (int32_t) UNREASONABLE_STACK_MOVE);
+ __ jcc(Assembler::less, L_ok);
+ } else {
+ __ jcc(allow_zero ? Assembler::greater : Assembler::greaterEqual, L_bad);
+ __ cmpptr(arg_slots.as_register(), (int32_t) -UNREASONABLE_STACK_MOVE);
+ __ jcc(Assembler::greater, L_ok);
+ }
+ __ bind(L_bad);
+ if (direction > 0)
+ __ stop("assert arg_slots > 0");
+ else
+ __ stop("assert arg_slots < 0");
+ __ BIND(L_ok);
+ BLOCK_COMMENT("} verify_stack_move");
+ } else {
+ intptr_t size = arg_slots.as_constant();
+ if (direction < 0) size = -size;
+ assert(size >= 0, "correct direction of constant move");
+ assert(size < UNREASONABLE_STACK_MOVE, "reasonable size of constant move");
+ }
+}
+
+void MethodHandles::verify_klass(MacroAssembler* _masm,
+ Register obj, KlassHandle klass,
+ const char* error_message) {
+ oop* klass_addr = klass.raw_value();
+ assert(klass_addr >= SystemDictionaryHandles::Object_klass().raw_value() &&
+ klass_addr <= SystemDictionaryHandles::Long_klass().raw_value(),
+ "must be one of the SystemDictionaryHandles");
+ Register temp = rdi;
+ Label L_ok, L_bad;
+ BLOCK_COMMENT("verify_klass {");
+ __ verify_oop(obj);
+ __ testptr(obj, obj);
+ __ jcc(Assembler::zero, L_bad);
+ __ push(temp);
+ __ load_klass(temp, obj);
+ __ cmpptr(temp, ExternalAddress((address) klass_addr));
+ __ jcc(Assembler::equal, L_ok);
+ intptr_t super_check_offset = klass->super_check_offset();
+ __ movptr(temp, Address(temp, super_check_offset));
+ __ cmpptr(temp, ExternalAddress((address) klass_addr));
+ __ jcc(Assembler::equal, L_ok);
+ __ pop(temp);
+ __ bind(L_bad);
+ __ stop(error_message);
+ __ BIND(L_ok);
+ __ pop(temp);
+ BLOCK_COMMENT("} verify_klass");
+}
+#endif //ASSERT
// Code generation
address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
@@ -116,6 +568,9 @@
address entry_point = __ pc();
// fetch the MethodType from the method handle into rax (the 'check' register)
+ // FIXME: Interpreter should transmit pre-popped stack pointer, to locate base of arg list.
+ // This would simplify several touchy bits of code.
+ // See 6984712: JSR 292 method handle calls need a clean argument base pointer
{
Register tem = rbx_method;
for (jint* pchase = methodOopDesc::method_type_offsets_chain(); (*pchase) != -1; pchase++) {
@@ -128,17 +583,23 @@
__ load_heap_oop(rdx_temp, Address(rax_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, rdi_temp)));
Register rdx_vmslots = rdx_temp;
__ movl(rdx_vmslots, Address(rdx_temp, __ delayed_value(java_lang_invoke_MethodTypeForm::vmslots_offset_in_bytes, rdi_temp)));
- __ movptr(rcx_recv, __ argument_address(rdx_vmslots));
+ Address mh_receiver_slot_addr = __ argument_address(rdx_vmslots);
+ __ movptr(rcx_recv, mh_receiver_slot_addr);
trace_method_handle(_masm, "invokeExact");
__ check_method_handle_type(rax_mtype, rcx_recv, rdi_temp, wrong_method_type);
+
+ // Nobody uses the MH receiver slot after this. Make sure.
+ DEBUG_ONLY(__ movptr(mh_receiver_slot_addr, (int32_t)0x999999));
+
__ jump_to_method_handle_entry(rcx_recv, rdi_temp);
// for invokeGeneric (only), apply argument and result conversions on the fly
__ bind(invoke_generic_slow_path);
#ifdef ASSERT
- { Label L;
+ if (VerifyMethodHandles) {
+ Label L;
__ cmpb(Address(rbx_method, methodOopDesc::intrinsic_id_offset_in_bytes()), (int) vmIntrinsics::_invokeGeneric);
__ jcc(Assembler::equal, L);
__ stop("bad methodOop::intrinsic_id");
@@ -150,22 +611,14 @@
// make room on the stack for another pointer:
Register rcx_argslot = rcx_recv;
__ lea(rcx_argslot, __ argument_address(rdx_vmslots, 1));
- insert_arg_slots(_masm, 2 * stack_move_unit(), _INSERT_REF_MASK,
+ insert_arg_slots(_masm, 2 * stack_move_unit(),
rcx_argslot, rbx_temp, rdx_temp);
// load up an adapter from the calling type (Java weaves this)
- __ load_heap_oop(rdx_temp, Address(rax_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, rdi_temp)));
Register rdx_adapter = rdx_temp;
- // __ load_heap_oop(rdx_adapter, Address(rdx_temp, java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes()));
- // deal with old JDK versions:
- __ lea(rdi_temp, Address(rdx_temp, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, rdi_temp)));
- __ cmpptr(rdi_temp, rdx_temp);
- Label sorry_no_invoke_generic;
- __ jcc(Assembler::below, sorry_no_invoke_generic);
-
- __ load_heap_oop(rdx_adapter, Address(rdi_temp, 0));
- __ testptr(rdx_adapter, rdx_adapter);
- __ jcc(Assembler::zero, sorry_no_invoke_generic);
+ __ load_heap_oop(rdx_temp, Address(rax_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, rdi_temp)));
+ __ load_heap_oop(rdx_adapter, Address(rdx_temp, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, rdi_temp)));
+ __ verify_oop(rdx_adapter);
__ movptr(Address(rcx_argslot, 1 * Interpreter::stackElementSize), rdx_adapter);
// As a trusted first argument, pass the type being called, so the adapter knows
// the actual types of the arguments and return values.
@@ -176,49 +629,31 @@
trace_method_handle(_masm, "invokeGeneric");
__ jump_to_method_handle_entry(rcx, rdi_temp);
- __ bind(sorry_no_invoke_generic); // no invokeGeneric implementation available!
- __ movptr(rcx_recv, Address(rcx_argslot, -1 * Interpreter::stackElementSize)); // recover original MH
- __ push(rax_mtype); // required mtype
- __ push(rcx_recv); // bad mh (1st stacked argument)
- __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry()));
+ return entry_point;
+}
- return entry_point;
+// Workaround for C++ overloading nastiness on '0' for RegisterOrConstant.
+static RegisterOrConstant constant(int value) {
+ return RegisterOrConstant(value);
}
// Helper to insert argument slots into the stack.
-// arg_slots must be a multiple of stack_move_unit() and <= 0
+// arg_slots must be a multiple of stack_move_unit() and < 0
+// rax_argslot is decremented to point to the new (shifted) location of the argslot
+// But, rdx_temp ends up holding the original value of rax_argslot.
void MethodHandles::insert_arg_slots(MacroAssembler* _masm,
RegisterOrConstant arg_slots,
- int arg_mask,
Register rax_argslot,
- Register rbx_temp, Register rdx_temp, Register temp3_reg) {
- assert(temp3_reg == noreg, "temp3 not required");
+ Register rbx_temp, Register rdx_temp) {
+ // allow constant zero
+ if (arg_slots.is_constant() && arg_slots.as_constant() == 0)
+ return;
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);
- __ jccb(Assembler::greater, L_bad);
- __ testl(arg_slots.as_register(), -stack_move_unit() - 1);
- __ jccb(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
+ if (VerifyMethodHandles)
+ verify_argslot(_masm, rax_argslot, "insertion point must fall within current frame");
+ if (VerifyMethodHandles)
+ verify_stack_move(_masm, arg_slots, -1);
// Make space on the stack for the inserted argument(s).
// Then pull down everything shallower than rax_argslot.
@@ -230,59 +665,39 @@
// argslot -= size;
BLOCK_COMMENT("insert_arg_slots {");
__ mov(rdx_temp, rsp); // source pointer for copy
- __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr));
+ __ lea(rsp, Address(rsp, arg_slots, Interpreter::stackElementScale()));
{
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);
+ __ movptr(Address(rdx_temp, arg_slots, Interpreter::stackElementScale()), rbx_temp);
__ addptr(rdx_temp, wordSize);
__ cmpptr(rdx_temp, rax_argslot);
- __ jccb(Assembler::less, loop);
+ __ jcc(Assembler::below, loop);
}
// Now move the argslot down, to point to the opened-up space.
- __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr));
+ __ lea(rax_argslot, Address(rax_argslot, arg_slots, Interpreter::stackElementScale()));
BLOCK_COMMENT("} insert_arg_slots");
}
// Helper to remove argument slots from the stack.
-// arg_slots must be a multiple of stack_move_unit() and >= 0
+// 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, Register temp3_reg) {
- assert(temp3_reg == noreg, "temp3 not required");
+ RegisterOrConstant arg_slots,
+ Register rax_argslot,
+ Register rbx_temp, Register rdx_temp) {
+ // allow constant zero
+ if (arg_slots.is_constant() && arg_slots.as_constant() == 0)
+ return;
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).
- __ lea(rbx_temp, Address(rax_argslot, arg_slots, Address::times_ptr));
- verify_argslot(_masm, rbx_temp, "deleted argument(s) must fall within current frame");
- if (arg_slots.is_register()) {
- Label L_ok, L_bad;
- __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD);
- __ jccb(Assembler::less, L_bad);
- __ testl(arg_slots.as_register(), -stack_move_unit() - 1);
- __ jccb(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
+ if (VerifyMethodHandles)
+ verify_argslots(_masm, arg_slots, rax_argslot, false,
+ "deleted argument(s) must fall within current frame");
+ if (VerifyMethodHandles)
+ verify_stack_move(_masm, arg_slots, +1);
BLOCK_COMMENT("remove_arg_slots {");
// Pull up everything shallower than rax_argslot.
@@ -299,54 +714,344 @@
__ 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);
+ __ movptr(Address(rdx_temp, arg_slots, Interpreter::stackElementScale()), rbx_temp);
__ addptr(rdx_temp, -wordSize);
__ cmpptr(rdx_temp, rsp);
- __ jccb(Assembler::greaterEqual, loop);
+ __ jcc(Assembler::aboveEqual, loop);
}
// Now move the argslot up, to point to the just-copied block.
- __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr));
+ __ lea(rsp, Address(rsp, arg_slots, Interpreter::stackElementScale()));
// And adjust the argslot address to point at the deletion point.
- __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr));
+ __ lea(rax_argslot, Address(rax_argslot, arg_slots, Interpreter::stackElementScale()));
BLOCK_COMMENT("} remove_arg_slots");
}
+// Helper to copy argument slots to the top of the stack.
+// The sequence starts with rax_argslot and is counted by slot_count
+// slot_count must be a multiple of stack_move_unit() and >= 0
+// This function blows the temps but does not change rax_argslot.
+void MethodHandles::push_arg_slots(MacroAssembler* _masm,
+ Register rax_argslot,
+ RegisterOrConstant slot_count,
+ int skip_words_count,
+ Register rbx_temp, Register rdx_temp) {
+ assert_different_registers(rax_argslot, rbx_temp, rdx_temp,
+ (!slot_count.is_register() ? rbp : slot_count.as_register()),
+ rsp);
+ assert(Interpreter::stackElementSize == wordSize, "else change this code");
+
+ if (VerifyMethodHandles)
+ verify_stack_move(_masm, slot_count, 0);
+
+ // allow constant zero
+ if (slot_count.is_constant() && slot_count.as_constant() == 0)
+ return;
+
+ BLOCK_COMMENT("push_arg_slots {");
+
+ Register rbx_top = rbx_temp;
+
+ // There is at most 1 word to carry down with the TOS.
+ switch (skip_words_count) {
+ case 1: __ pop(rdx_temp); break;
+ case 0: break;
+ default: ShouldNotReachHere();
+ }
+
+ if (slot_count.is_constant()) {
+ for (int i = slot_count.as_constant() - 1; i >= 0; i--) {
+ __ pushptr(Address(rax_argslot, i * wordSize));
+ }
+ } else {
+ Label L_plural, L_loop, L_break;
+ // Emit code to dynamically check for the common cases, zero and one slot.
+ __ cmpl(slot_count.as_register(), (int32_t) 1);
+ __ jccb(Assembler::greater, L_plural);
+ __ jccb(Assembler::less, L_break);
+ __ pushptr(Address(rax_argslot, 0));
+ __ jmpb(L_break);
+ __ BIND(L_plural);
+
+ // Loop for 2 or more:
+ // rbx = &rax[slot_count]
+ // while (rbx > rax) *(--rsp) = *(--rbx)
+ __ lea(rbx_top, Address(rax_argslot, slot_count, Address::times_ptr));
+ __ BIND(L_loop);
+ __ subptr(rbx_top, wordSize);
+ __ pushptr(Address(rbx_top, 0));
+ __ cmpptr(rbx_top, rax_argslot);
+ __ jcc(Assembler::above, L_loop);
+ __ bind(L_break);
+ }
+ switch (skip_words_count) {
+ case 1: __ push(rdx_temp); break;
+ case 0: break;
+ default: ShouldNotReachHere();
+ }
+ BLOCK_COMMENT("} push_arg_slots");
+}
+
+// in-place movement; no change to rsp
+// blows rax_temp, rdx_temp
+void MethodHandles::move_arg_slots_up(MacroAssembler* _masm,
+ Register rbx_bottom, // invariant
+ Address top_addr, // can use rax_temp
+ RegisterOrConstant positive_distance_in_slots,
+ Register rax_temp, Register rdx_temp) {
+ BLOCK_COMMENT("move_arg_slots_up {");
+ assert_different_registers(rbx_bottom,
+ rax_temp, rdx_temp,
+ positive_distance_in_slots.register_or_noreg());
+ Label L_loop, L_break;
+ Register rax_top = rax_temp;
+ if (!top_addr.is_same_address(Address(rax_top, 0)))
+ __ lea(rax_top, top_addr);
+ // Detect empty (or broken) loop:
+#ifdef ASSERT
+ if (VerifyMethodHandles) {
+ // Verify that &bottom < &top (non-empty interval)
+ Label L_ok, L_bad;
+ if (positive_distance_in_slots.is_register()) {
+ __ cmpptr(positive_distance_in_slots.as_register(), (int32_t) 0);
+ __ jcc(Assembler::lessEqual, L_bad);
+ }
+ __ cmpptr(rbx_bottom, rax_top);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_bad);
+ __ stop("valid bounds (copy up)");
+ __ BIND(L_ok);
+ }
+#endif
+ __ cmpptr(rbx_bottom, rax_top);
+ __ jccb(Assembler::aboveEqual, L_break);
+ // work rax down to rbx, copying contiguous data upwards
+ // In pseudo-code:
+ // [rbx, rax) = &[bottom, top)
+ // while (--rax >= rbx) *(rax + distance) = *(rax + 0), rax--;
+ __ BIND(L_loop);
+ __ subptr(rax_top, wordSize);
+ __ movptr(rdx_temp, Address(rax_top, 0));
+ __ movptr( Address(rax_top, positive_distance_in_slots, Address::times_ptr), rdx_temp);
+ __ cmpptr(rax_top, rbx_bottom);
+ __ jcc(Assembler::above, L_loop);
+ assert(Interpreter::stackElementSize == wordSize, "else change loop");
+ __ bind(L_break);
+ BLOCK_COMMENT("} move_arg_slots_up");
+}
+
+// in-place movement; no change to rsp
+// blows rax_temp, rdx_temp
+void MethodHandles::move_arg_slots_down(MacroAssembler* _masm,
+ Address bottom_addr, // can use rax_temp
+ Register rbx_top, // invariant
+ RegisterOrConstant negative_distance_in_slots,
+ Register rax_temp, Register rdx_temp) {
+ BLOCK_COMMENT("move_arg_slots_down {");
+ assert_different_registers(rbx_top,
+ negative_distance_in_slots.register_or_noreg(),
+ rax_temp, rdx_temp);
+ Label L_loop, L_break;
+ Register rax_bottom = rax_temp;
+ if (!bottom_addr.is_same_address(Address(rax_bottom, 0)))
+ __ lea(rax_bottom, bottom_addr);
+ // Detect empty (or broken) loop:
+#ifdef ASSERT
+ assert(!negative_distance_in_slots.is_constant() || negative_distance_in_slots.as_constant() < 0, "");
+ if (VerifyMethodHandles) {
+ // Verify that &bottom < &top (non-empty interval)
+ Label L_ok, L_bad;
+ if (negative_distance_in_slots.is_register()) {
+ __ cmpptr(negative_distance_in_slots.as_register(), (int32_t) 0);
+ __ jcc(Assembler::greaterEqual, L_bad);
+ }
+ __ cmpptr(rax_bottom, rbx_top);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_bad);
+ __ stop("valid bounds (copy down)");
+ __ BIND(L_ok);
+ }
+#endif
+ __ cmpptr(rax_bottom, rbx_top);
+ __ jccb(Assembler::aboveEqual, L_break);
+ // work rax up to rbx, copying contiguous data downwards
+ // In pseudo-code:
+ // [rax, rbx) = &[bottom, top)
+ // while (rax < rbx) *(rax - distance) = *(rax + 0), rax++;
+ __ BIND(L_loop);
+ __ movptr(rdx_temp, Address(rax_bottom, 0));
+ __ movptr( Address(rax_bottom, negative_distance_in_slots, Address::times_ptr), rdx_temp);
+ __ addptr(rax_bottom, wordSize);
+ __ cmpptr(rax_bottom, rbx_top);
+ __ jcc(Assembler::below, L_loop);
+ assert(Interpreter::stackElementSize == wordSize, "else change loop");
+ __ bind(L_break);
+ BLOCK_COMMENT("} move_arg_slots_down");
+}
+
+// Copy from a field or array element to a stacked argument slot.
+// is_element (ignored) says whether caller is loading an array element instead of an instance field.
+void MethodHandles::move_typed_arg(MacroAssembler* _masm,
+ BasicType type, bool is_element,
+ Address slot_dest, Address value_src,
+ Register rbx_temp, Register rdx_temp) {
+ BLOCK_COMMENT(!is_element ? "move_typed_arg {" : "move_typed_arg { (array element)");
+ if (type == T_OBJECT || type == T_ARRAY) {
+ __ load_heap_oop(rbx_temp, value_src);
+ __ movptr(slot_dest, rbx_temp);
+ } else if (type != T_VOID) {
+ int arg_size = type2aelembytes(type);
+ bool arg_is_signed = is_signed_subword_type(type);
+ int slot_size = (arg_size > wordSize) ? arg_size : wordSize;
+ __ load_sized_value( rdx_temp, value_src, arg_size, arg_is_signed, rbx_temp);
+ __ store_sized_value( slot_dest, rdx_temp, slot_size, rbx_temp);
+ }
+ BLOCK_COMMENT("} move_typed_arg");
+}
+
+void MethodHandles::move_return_value(MacroAssembler* _masm, BasicType type,
+ Address return_slot) {
+ BLOCK_COMMENT("move_return_value {");
+ // Old versions of the JVM must clean the FPU stack after every return.
+#ifndef _LP64
+#ifdef COMPILER2
+ // The FPU stack is clean if UseSSE >= 2 but must be cleaned in other cases
+ if ((type == T_FLOAT && UseSSE < 1) || (type == T_DOUBLE && UseSSE < 2)) {
+ for (int i = 1; i < 8; i++) {
+ __ ffree(i);
+ }
+ } else if (UseSSE < 2) {
+ __ empty_FPU_stack();
+ }
+#endif //COMPILER2
+#endif //!_LP64
+
+ // Look at the type and pull the value out of the corresponding register.
+ if (type == T_VOID) {
+ // nothing to do
+ } else if (type == T_OBJECT) {
+ __ movptr(return_slot, rax);
+ } else if (type == T_INT || is_subword_type(type)) {
+ // write the whole word, even if only 32 bits is significant
+ __ movptr(return_slot, rax);
+ } else if (type == T_LONG) {
+ // store the value by parts
+ // Note: We assume longs are continguous (if misaligned) on the interpreter stack.
+ __ store_sized_value(return_slot, rax, BytesPerLong, rdx);
+ } else if (NOT_LP64((type == T_FLOAT && UseSSE < 1) ||
+ (type == T_DOUBLE && UseSSE < 2) ||)
+ false) {
+ // Use old x86 FPU registers:
+ if (type == T_FLOAT)
+ __ fstp_s(return_slot);
+ else
+ __ fstp_d(return_slot);
+ } else if (type == T_FLOAT) {
+ __ movflt(return_slot, xmm0);
+ } else if (type == T_DOUBLE) {
+ __ movdbl(return_slot, xmm0);
+ } else {
+ ShouldNotReachHere();
+ }
+ BLOCK_COMMENT("} move_return_value");
+}
+
+
#ifndef PRODUCT
extern "C" void print_method_handle(oop mh);
void trace_method_handle_stub(const char* adaptername,
+ oop mh,
+ intptr_t* saved_regs,
+ intptr_t* entry_sp,
intptr_t* saved_sp,
- oop mh,
- intptr_t* sp) {
+ intptr_t* saved_bp) {
// called as a leaf from native code: do not block the JVM!
- intptr_t* entry_sp = sp + LP64_ONLY(16) NOT_LP64(8);
- tty->print_cr("MH %s mh="INTPTR_FORMAT" sp="INTPTR_FORMAT" saved_sp="INTPTR_FORMAT")",
- adaptername, (intptr_t)mh, (intptr_t)entry_sp, saved_sp);
+ bool has_mh = (strstr(adaptername, "return/") == NULL); // return adapters don't have rcx_mh
+ intptr_t* last_sp = (intptr_t*) saved_bp[frame::interpreter_frame_last_sp_offset];
+ intptr_t* base_sp = last_sp;
+ typedef MethodHandles::RicochetFrame RicochetFrame;
+ RicochetFrame* rfp = (RicochetFrame*)((address)saved_bp - RicochetFrame::sender_link_offset_in_bytes());
+ if (!UseRicochetFrames || Universe::heap()->is_in((address) rfp->saved_args_base())) {
+ // Probably an interpreter frame.
+ base_sp = (intptr_t*) saved_bp[frame::interpreter_frame_monitor_block_top_offset];
+ }
+ intptr_t mh_reg = (intptr_t)mh;
+ const char* mh_reg_name = "rcx_mh";
+ if (!has_mh) mh_reg_name = "rcx";
+ tty->print_cr("MH %s %s="PTR_FORMAT" sp=("PTR_FORMAT"+"INTX_FORMAT") stack_size="INTX_FORMAT" bp="PTR_FORMAT,
+ adaptername, mh_reg_name, mh_reg,
+ (intptr_t)entry_sp, (intptr_t)(saved_sp - entry_sp), (intptr_t)(base_sp - last_sp), (intptr_t)saved_bp);
if (Verbose) {
- print_method_handle(mh);
+ tty->print(" reg dump: ");
+ int saved_regs_count = (entry_sp-1) - saved_regs;
+ // 32 bit: rdi rsi rbp rsp; rbx rdx rcx (*) rax
+ int i;
+ for (i = 0; i <= saved_regs_count; i++) {
+ if (i > 0 && i % 4 == 0 && i != saved_regs_count) {
+ tty->cr();
+ tty->print(" + dump: ");
+ }
+ tty->print(" %d: "PTR_FORMAT, i, saved_regs[i]);
+ }
+ tty->cr();
+ if (last_sp != saved_sp && last_sp != NULL)
+ tty->print_cr("*** last_sp="PTR_FORMAT, (intptr_t)last_sp);
+ int stack_dump_count = 16;
+ if (stack_dump_count < (int)(saved_bp + 2 - saved_sp))
+ stack_dump_count = (int)(saved_bp + 2 - saved_sp);
+ if (stack_dump_count > 64) stack_dump_count = 48;
+ for (i = 0; i < stack_dump_count; i += 4) {
+ tty->print_cr(" dump at SP[%d] "PTR_FORMAT": "PTR_FORMAT" "PTR_FORMAT" "PTR_FORMAT" "PTR_FORMAT,
+ i, (intptr_t) &entry_sp[i+0], entry_sp[i+0], entry_sp[i+1], entry_sp[i+2], entry_sp[i+3]);
+ }
+ if (has_mh)
+ print_method_handle(mh);
}
}
+
+// The stub wraps the arguments in a struct on the stack to avoid
+// dealing with the different calling conventions for passing 6
+// arguments.
+struct MethodHandleStubArguments {
+ const char* adaptername;
+ oopDesc* mh;
+ intptr_t* saved_regs;
+ intptr_t* entry_sp;
+ intptr_t* saved_sp;
+ intptr_t* saved_bp;
+};
+void trace_method_handle_stub_wrapper(MethodHandleStubArguments* args) {
+ trace_method_handle_stub(args->adaptername,
+ args->mh,
+ args->saved_regs,
+ args->entry_sp,
+ args->saved_sp,
+ args->saved_bp);
+}
+
void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adaptername) {
if (!TraceMethodHandles) return;
BLOCK_COMMENT("trace_method_handle {");
+ __ push(rax);
+ __ lea(rax, Address(rsp, wordSize * NOT_LP64(6) LP64_ONLY(14))); // entry_sp __ pusha();
__ pusha();
-#ifdef _LP64
- // Pass arguments carefully since the registers overlap with the calling convention.
+ __ mov(rbx, rsp);
+ __ enter();
+ // incoming state:
// rcx: method handle
- // r13: saved sp
- __ mov(c_rarg2, rcx); // mh
- __ mov(c_rarg1, r13); // saved sp
- __ mov(c_rarg3, rsp); // sp
- __ movptr(c_rarg0, (intptr_t) adaptername);
- __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), c_rarg0, c_rarg1, c_rarg2, c_rarg3);
-#else
- // arguments:
- // rcx: method handle
- // rsi: saved sp
- __ movptr(rbx, (intptr_t) adaptername);
- __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), rbx, rsi, rcx, rsp);
-#endif
+ // r13 or rsi: saved sp
+ // To avoid calling convention issues, build a record on the stack and pass the pointer to that instead.
+ __ push(rbp); // saved_bp
+ __ push(rsi); // saved_sp
+ __ push(rax); // entry_sp
+ __ push(rbx); // pusha saved_regs
+ __ push(rcx); // mh
+ __ push(rcx); // adaptername
+ __ movptr(Address(rsp, 0), (intptr_t) adaptername);
+ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub_wrapper), rsp);
+ __ leave();
__ popa();
+ __ pop(rax);
BLOCK_COMMENT("} trace_method_handle");
}
#endif //PRODUCT
@@ -358,13 +1063,20 @@
|(1<<java_lang_invoke_AdapterMethodHandle::OP_CHECK_CAST)
|(1<<java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_PRIM)
|(1<<java_lang_invoke_AdapterMethodHandle::OP_REF_TO_PRIM)
+ //OP_PRIM_TO_REF is below...
|(1<<java_lang_invoke_AdapterMethodHandle::OP_SWAP_ARGS)
|(1<<java_lang_invoke_AdapterMethodHandle::OP_ROT_ARGS)
|(1<<java_lang_invoke_AdapterMethodHandle::OP_DUP_ARGS)
|(1<<java_lang_invoke_AdapterMethodHandle::OP_DROP_ARGS)
- //|(1<<java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS) //BUG!
+ //OP_COLLECT_ARGS is below...
+ |(1<<java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS)
+ |(!UseRicochetFrames ? 0 :
+ java_lang_invoke_MethodTypeForm::vmlayout_offset_in_bytes() <= 0 ? 0 :
+ ((1<<java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF)
+ |(1<<java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS)
+ |(1<<java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS)
+ ))
);
- // FIXME: MethodHandlesTest gets a crash if we enable OP_SPREAD_ARGS.
}
//------------------------------------------------------------------------------
@@ -373,6 +1085,8 @@
// 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) {
+ MethodHandles::EntryKind ek_orig = ek_original_kind(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)
@@ -385,10 +1099,11 @@
const Register rax_argslot = rax;
const Register rbx_temp = rbx;
const Register rdx_temp = rdx;
+ const Register rdi_temp = rdi;
// This guy is set up by prepare_to_jump_from_interpreted (from interpreted calls)
// and gen_c2i_adapter (from compiled calls):
- const Register saved_last_sp = LP64_ONLY(r13) NOT_LP64(rsi);
+ const Register saved_last_sp = saved_last_sp_register();
// Argument registers for _raise_exception.
// 32-bit: Pass first two oop/int args in registers ECX and EDX.
@@ -421,6 +1136,13 @@
return;
}
+#ifdef ASSERT
+ __ push((int32_t) 0xEEEEEEEE);
+ __ push((int32_t) (intptr_t) entry_name(ek));
+ LP64_ONLY(__ push((int32_t) high((intptr_t) entry_name(ek))));
+ __ push((int32_t) 0x33333333);
+#endif //ASSERT
+
address interp_entry = __ pc();
trace_method_handle(_masm, entry_name(ek));
@@ -536,7 +1258,6 @@
__ load_klass(rax_klass, rcx_recv);
__ verify_oop(rax_klass);
- Register rdi_temp = rdi;
Register rbx_method = rbx_index;
// get interface klass
@@ -572,16 +1293,14 @@
case _bound_long_direct_mh:
{
bool direct_to_method = (ek >= _bound_ref_direct_mh);
- BasicType arg_type = T_ILLEGAL;
- int arg_mask = _INSERT_NO_MASK;
- int arg_slots = -1;
- get_ek_bound_mh_info(ek, arg_type, arg_mask, arg_slots);
+ BasicType arg_type = ek_bound_mh_arg_type(ek);
+ int arg_slots = type2size[arg_type];
// 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);
+ insert_arg_slots(_masm, arg_slots * stack_move_unit(), rax_argslot, rbx_temp, rdx_temp);
// store bound argument into the new stack slot:
__ load_heap_oop(rbx_temp, rcx_bmh_argument);
@@ -589,9 +1308,10 @@
__ movptr(Address(rax_argslot, 0), rbx_temp);
} else {
Address prim_value_addr(rbx_temp, java_lang_boxing_object::value_offset_in_bytes(arg_type));
- const int arg_size = type2aelembytes(arg_type);
- __ load_sized_value(rdx_temp, prim_value_addr, arg_size, is_signed_subword_type(arg_type), rbx_temp);
- __ store_sized_value(Address(rax_argslot, 0), rdx_temp, arg_size, rbx_temp);
+ move_typed_arg(_masm, arg_type, false,
+ Address(rax_argslot, 0),
+ prim_value_addr,
+ rbx_temp, rdx_temp);
}
if (direct_to_method) {
@@ -628,7 +1348,7 @@
// What class are we casting to?
__ load_heap_oop(rbx_klass, rcx_amh_argument); // this is a Class object!
- __ load_heap_oop(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes()));
+ load_klass_from_Class(_masm, rbx_klass);
Label done;
__ movptr(rdx_temp, vmarg);
@@ -663,6 +1383,7 @@
case _adapter_prim_to_prim:
case _adapter_ref_to_prim:
+ case _adapter_prim_to_ref:
// handled completely by optimized cases
__ stop("init_AdapterMethodHandle should not issue this");
break;
@@ -714,8 +1435,7 @@
// Do the requested conversion and store the value.
Register rbx_vminfo = rbx_temp;
- __ movl(rbx_vminfo, rcx_amh_conversion);
- assert(CONV_VMINFO_SHIFT == 0, "preshifted");
+ load_conversion_vminfo(_masm, rbx_vminfo, rcx_amh_conversion);
// get the new MH:
__ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
@@ -753,7 +1473,7 @@
// 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,
+ insert_arg_slots(_masm, stack_move_unit(),
rax_argslot, rbx_temp, rdx_temp);
Address vmarg1(rax_argslot, -Interpreter::stackElementSize);
Address vmarg2 = vmarg1.plus_disp(Interpreter::stackElementSize);
@@ -805,7 +1525,7 @@
__ 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,
+ insert_arg_slots(_masm, stack_move_unit(),
rax_argslot, rbx_temp, rdx_temp);
}
Address vmarg(rax_argslot, -Interpreter::stackElementSize);
@@ -823,7 +1543,7 @@
#else //_LP64
if (ek == _adapter_opt_f2d) {
__ fld_s(vmarg); // load float to ST0
- __ fstp_s(vmarg); // store single
+ __ fstp_d(vmarg); // store double
} else {
__ fld_d(vmarg); // load double to ST0
__ fstp_s(vmarg); // store single
@@ -840,10 +1560,6 @@
}
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
@@ -857,8 +1573,8 @@
case _adapter_opt_rot_2_up:
case _adapter_opt_rot_2_down:
{
- int swap_bytes = 0, rotate = 0;
- get_ek_adapter_opt_swap_rot_info(ek, swap_bytes, rotate);
+ int swap_slots = ek_adapter_opt_swap_slots(ek);
+ int rotate = ek_adapter_opt_swap_mode(ek);
// 'argslot' is the position of the first argument to swap
__ movl(rax_argslot, rcx_amh_vmargslot);
@@ -866,83 +1582,69 @@
// '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);
+ load_conversion_vminfo(_masm, rbx_destslot, rcx_amh_conversion);
__ lea(rbx_destslot, __ argument_address(rbx_destslot));
- DEBUG_ONLY(verify_argslot(_masm, rbx_destslot, "swap point must fall within current frame"));
+ if (VerifyMethodHandles)
+ verify_argslot(_masm, rbx_destslot, "swap point must fall within current frame");
+ assert(Interpreter::stackElementSize == wordSize, "else rethink use of wordSize here");
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);
+ // simple swap
+ for (int i = 0; i < swap_slots; i++) {
+ __ movptr(rdi_temp, Address(rax_argslot, i * wordSize));
+ __ movptr(rdx_temp, Address(rbx_destslot, i * wordSize));
+ __ movptr(Address(rax_argslot, i * wordSize), rdx_temp);
+ __ movptr(Address(rbx_destslot, i * wordSize), rdi_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);
+ // A rotate is actually pair of moves, with an "odd slot" (or pair)
+ // changing place with a series of other slots.
+ // First, push the "odd slot", which is going to get overwritten
+ for (int i = swap_slots - 1; i >= 0; i--) {
+ // handle one with rdi_temp instead of a push:
+ if (i == 0) __ movptr(rdi_temp, Address(rax_argslot, i * wordSize));
+ else __ pushptr( Address(rax_argslot, i * wordSize));
}
-
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);
- __ jccb(Assembler::aboveEqual, L_ok);
- __ stop("source must be above destination (upward rotation)");
- __ bind(L_ok);
- }
-#endif
+ // Here is rotate > 0:
+ // (low mem) (high mem)
+ // | dest: more_slots... | arg: odd_slot :arg+1 |
+ // =>
+ // | dest: odd_slot | dest+1: more_slots... :arg+1 |
// 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);
- __ jccb(Assembler::aboveEqual, loop);
+ move_arg_slots_up(_masm,
+ rbx_destslot,
+ Address(rax_argslot, 0),
+ swap_slots,
+ rax_argslot, rdx_temp);
} 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);
- __ jccb(Assembler::belowEqual, L_ok);
- __ stop("source must be below destination (downward rotation)");
- __ bind(L_ok);
- }
-#endif
+ // Here is the other direction, rotate < 0:
+ // (low mem) (high mem)
+ // | arg: odd_slot | arg+1: more_slots... :dest+1 |
+ // =>
+ // | arg: more_slots... | dest: odd_slot :dest+1 |
// 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);
- __ jccb(Assembler::belowEqual, loop);
+ __ addptr(rbx_destslot, wordSize);
+ move_arg_slots_down(_masm,
+ Address(rax_argslot, swap_slots * wordSize),
+ rbx_destslot,
+ -swap_slots,
+ rax_argslot, rdx_temp);
+
+ __ subptr(rbx_destslot, wordSize);
}
-
// 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);
+ for (int i = 0; i < swap_slots; i++) {
+ if (i == 0) __ movptr(Address(rbx_destslot, i * wordSize), rdi_temp);
+ else __ popptr(Address(rbx_destslot, i * wordSize));
}
}
@@ -958,53 +1660,22 @@
__ lea(rax_argslot, __ argument_address(rax_argslot));
// 'stack_move' is negative number of words to duplicate
- Register rdx_stack_move = rdx_temp;
- __ movl2ptr(rdx_stack_move, rcx_amh_conversion);
- __ sarptr(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);
+ Register rdi_stack_move = rdi_temp;
+ load_stack_move(_masm, rdi_stack_move, rcx_recv, true);
- // 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);
+ if (VerifyMethodHandles) {
+ verify_argslots(_masm, rdi_stack_move, rax_argslot, true,
+ "copied argument(s) must fall within current frame");
}
- // 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);
- __ jccb(Assembler::less, loop);
+ // insert location is always the bottom of the argument list:
+ Address insert_location = __ argument_address(constant(0));
+ int pre_arg_words = insert_location.disp() / wordSize; // return PC is pushed
+ assert(insert_location.base() == rsp, "");
- __ pop(rdi); // restore temp
+ __ negl(rdi_stack_move);
+ push_arg_slots(_masm, rax_argslot, rdi_stack_move,
+ pre_arg_words, rbx_temp, rdx_temp);
__ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
__ jump_to_method_handle_entry(rcx_recv, rdx_temp);
@@ -1017,63 +1688,583 @@
__ 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;
- __ movl2ptr(rdi_stack_move, rcx_amh_conversion);
- __ sarptr(rdi_stack_move, CONV_STACK_MOVE_SHIFT);
+ Register rdi_stack_move = rdi_temp;
+ load_stack_move(_masm, rdi_stack_move, rcx_recv, false);
remove_arg_slots(_masm, rdi_stack_move,
rax_argslot, rbx_temp, rdx_temp);
- __ pop(rdi); // restore temp
-
__ load_heap_oop(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_fold_args:
case _adapter_spread_args:
// handled completely by optimized cases
__ stop("init_AdapterMethodHandle should not issue this");
break;
+ case _adapter_opt_collect_ref:
+ case _adapter_opt_collect_int:
+ case _adapter_opt_collect_long:
+ case _adapter_opt_collect_float:
+ case _adapter_opt_collect_double:
+ case _adapter_opt_collect_void:
+ case _adapter_opt_collect_0_ref:
+ case _adapter_opt_collect_1_ref:
+ case _adapter_opt_collect_2_ref:
+ case _adapter_opt_collect_3_ref:
+ case _adapter_opt_collect_4_ref:
+ case _adapter_opt_collect_5_ref:
+ case _adapter_opt_filter_S0_ref:
+ case _adapter_opt_filter_S1_ref:
+ case _adapter_opt_filter_S2_ref:
+ case _adapter_opt_filter_S3_ref:
+ case _adapter_opt_filter_S4_ref:
+ case _adapter_opt_filter_S5_ref:
+ case _adapter_opt_collect_2_S0_ref:
+ case _adapter_opt_collect_2_S1_ref:
+ case _adapter_opt_collect_2_S2_ref:
+ case _adapter_opt_collect_2_S3_ref:
+ case _adapter_opt_collect_2_S4_ref:
+ case _adapter_opt_collect_2_S5_ref:
+ case _adapter_opt_fold_ref:
+ case _adapter_opt_fold_int:
+ case _adapter_opt_fold_long:
+ case _adapter_opt_fold_float:
+ case _adapter_opt_fold_double:
+ case _adapter_opt_fold_void:
+ case _adapter_opt_fold_1_ref:
+ case _adapter_opt_fold_2_ref:
+ case _adapter_opt_fold_3_ref:
+ case _adapter_opt_fold_4_ref:
+ case _adapter_opt_fold_5_ref:
+ {
+ // Given a fresh incoming stack frame, build a new ricochet frame.
+ // On entry, TOS points at a return PC, and RBP is the callers frame ptr.
+ // RSI/R13 has the caller's exact stack pointer, which we must also preserve.
+ // RCX contains an AdapterMethodHandle of the indicated kind.
+
+ // Relevant AMH fields:
+ // amh.vmargslot:
+ // points to the trailing edge of the arguments
+ // to filter, collect, or fold. For a boxing operation,
+ // it points just after the single primitive value.
+ // amh.argument:
+ // recursively called MH, on |collect| arguments
+ // amh.vmtarget:
+ // final destination MH, on return value, etc.
+ // amh.conversion.dest:
+ // tells what is the type of the return value
+ // (not needed here, since dest is also derived from ek)
+ // amh.conversion.vminfo:
+ // points to the trailing edge of the return value
+ // when the vmtarget is to be called; this is
+ // equal to vmargslot + (retained ? |collect| : 0)
+
+ // Pass 0 or more argument slots to the recursive target.
+ int collect_count_constant = ek_adapter_opt_collect_count(ek);
+
+ // The collected arguments are copied from the saved argument list:
+ int collect_slot_constant = ek_adapter_opt_collect_slot(ek);
+
+ assert(ek_orig == _adapter_collect_args ||
+ ek_orig == _adapter_fold_args, "");
+ bool retain_original_args = (ek_orig == _adapter_fold_args);
+
+ // The return value is replaced (or inserted) at the 'vminfo' argslot.
+ // Sometimes we can compute this statically.
+ int dest_slot_constant = -1;
+ if (!retain_original_args)
+ dest_slot_constant = collect_slot_constant;
+ else if (collect_slot_constant >= 0 && collect_count_constant >= 0)
+ // We are preserving all the arguments, and the return value is prepended,
+ // so the return slot is to the left (above) the |collect| sequence.
+ dest_slot_constant = collect_slot_constant + collect_count_constant;
+
+ // Replace all those slots by the result of the recursive call.
+ // The result type can be one of ref, int, long, float, double, void.
+ // In the case of void, nothing is pushed on the stack after return.
+ BasicType dest = ek_adapter_opt_collect_type(ek);
+ assert(dest == type2wfield[dest], "dest is a stack slot type");
+ int dest_count = type2size[dest];
+ assert(dest_count == 1 || dest_count == 2 || (dest_count == 0 && dest == T_VOID), "dest has a size");
+
+ // Choose a return continuation.
+ EntryKind ek_ret = _adapter_opt_return_any;
+ if (dest != T_CONFLICT && OptimizeMethodHandles) {
+ switch (dest) {
+ case T_INT : ek_ret = _adapter_opt_return_int; break;
+ case T_LONG : ek_ret = _adapter_opt_return_long; break;
+ case T_FLOAT : ek_ret = _adapter_opt_return_float; break;
+ case T_DOUBLE : ek_ret = _adapter_opt_return_double; break;
+ case T_OBJECT : ek_ret = _adapter_opt_return_ref; break;
+ case T_VOID : ek_ret = _adapter_opt_return_void; break;
+ default : ShouldNotReachHere();
+ }
+ if (dest == T_OBJECT && dest_slot_constant >= 0) {
+ EntryKind ek_try = EntryKind(_adapter_opt_return_S0_ref + dest_slot_constant);
+ if (ek_try <= _adapter_opt_return_LAST &&
+ ek_adapter_opt_return_slot(ek_try) == dest_slot_constant) {
+ ek_ret = ek_try;
+ }
+ }
+ assert(ek_adapter_opt_return_type(ek_ret) == dest, "");
+ }
+
+ // Already pushed: ... keep1 | collect | keep2 | sender_pc |
+ // push(sender_pc);
+
+ // Compute argument base:
+ Register rax_argv = rax_argslot;
+ __ lea(rax_argv, __ argument_address(constant(0)));
+
+ // Push a few extra argument words, if we need them to store the return value.
+ {
+ int extra_slots = 0;
+ if (retain_original_args) {
+ extra_slots = dest_count;
+ } else if (collect_count_constant == -1) {
+ extra_slots = dest_count; // collect_count might be zero; be generous
+ } else if (dest_count > collect_count_constant) {
+ extra_slots = (dest_count - collect_count_constant);
+ } else {
+ // else we know we have enough dead space in |collect| to repurpose for return values
+ }
+ DEBUG_ONLY(extra_slots += 1);
+ if (extra_slots > 0) {
+ __ pop(rbx_temp); // return value
+ __ subptr(rsp, (extra_slots * Interpreter::stackElementSize));
+ // Push guard word #2 in debug mode.
+ DEBUG_ONLY(__ movptr(Address(rsp, 0), (int32_t) RicochetFrame::MAGIC_NUMBER_2));
+ __ push(rbx_temp);
+ }
+ }
+
+ RicochetFrame::enter_ricochet_frame(_masm, rcx_recv, rax_argv,
+ entry(ek_ret)->from_interpreted_entry(), rbx_temp);
+
+ // Now pushed: ... keep1 | collect | keep2 | RF |
+ // some handy frame slots:
+ Address exact_sender_sp_addr = RicochetFrame::frame_address(RicochetFrame::exact_sender_sp_offset_in_bytes());
+ Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes());
+ Address saved_args_base_addr = RicochetFrame::frame_address(RicochetFrame::saved_args_base_offset_in_bytes());
+
+#ifdef ASSERT
+ if (VerifyMethodHandles && dest != T_CONFLICT) {
+ BLOCK_COMMENT("verify AMH.conv.dest");
+ load_conversion_dest_type(_masm, rbx_temp, conversion_addr);
+ Label L_dest_ok;
+ __ cmpl(rbx_temp, (int) dest);
+ __ jcc(Assembler::equal, L_dest_ok);
+ if (dest == T_INT) {
+ for (int bt = T_BOOLEAN; bt < T_INT; bt++) {
+ if (is_subword_type(BasicType(bt))) {
+ __ cmpl(rbx_temp, (int) bt);
+ __ jcc(Assembler::equal, L_dest_ok);
+ }
+ }
+ }
+ __ stop("bad dest in AMH.conv");
+ __ BIND(L_dest_ok);
+ }
+#endif //ASSERT
+
+ // Find out where the original copy of the recursive argument sequence begins.
+ Register rax_coll = rax_argv;
+ {
+ RegisterOrConstant collect_slot = collect_slot_constant;
+ if (collect_slot_constant == -1) {
+ __ movl(rdi_temp, rcx_amh_vmargslot);
+ collect_slot = rdi_temp;
+ }
+ if (collect_slot_constant != 0)
+ __ lea(rax_coll, Address(rax_argv, collect_slot, Interpreter::stackElementScale()));
+ // rax_coll now points at the trailing edge of |collect| and leading edge of |keep2|
+ }
+
+ // Replace the old AMH with the recursive MH. (No going back now.)
+ // In the case of a boxing call, the recursive call is to a 'boxer' method,
+ // such as Integer.valueOf or Long.valueOf. In the case of a filter
+ // or collect call, it will take one or more arguments, transform them,
+ // and return some result, to store back into argument_base[vminfo].
+ __ load_heap_oop(rcx_recv, rcx_amh_argument);
+ if (VerifyMethodHandles) verify_method_handle(_masm, rcx_recv);
+
+ // Push a space for the recursively called MH first:
+ __ push((int32_t)NULL_WORD);
+
+ // Calculate |collect|, the number of arguments we are collecting.
+ Register rdi_collect_count = rdi_temp;
+ RegisterOrConstant collect_count;
+ if (collect_count_constant >= 0) {
+ collect_count = collect_count_constant;
+ } else {
+ __ load_method_handle_vmslots(rdi_collect_count, rcx_recv, rdx_temp);
+ collect_count = rdi_collect_count;
+ }
+#ifdef ASSERT
+ if (VerifyMethodHandles && collect_count_constant >= 0) {
+ __ load_method_handle_vmslots(rbx_temp, rcx_recv, rdx_temp);
+ Label L_count_ok;
+ __ cmpl(rbx_temp, collect_count_constant);
+ __ jcc(Assembler::equal, L_count_ok);
+ __ stop("bad vminfo in AMH.conv");
+ __ BIND(L_count_ok);
+ }
+#endif //ASSERT
+
+ // copy |collect| slots directly to TOS:
+ push_arg_slots(_masm, rax_coll, collect_count, 0, rbx_temp, rdx_temp);
+ // Now pushed: ... keep1 | collect | keep2 | RF... | collect |
+ // rax_coll still points at the trailing edge of |collect| and leading edge of |keep2|
+
+ // If necessary, adjust the saved arguments to make room for the eventual return value.
+ // Normal adjustment: ... keep1 | +dest+ | -collect- | keep2 | RF... | collect |
+ // If retaining args: ... keep1 | +dest+ | collect | keep2 | RF... | collect |
+ // In the non-retaining case, this might move keep2 either up or down.
+ // We don't have to copy the whole | RF... collect | complex,
+ // but we must adjust RF.saved_args_base.
+ // Also, from now on, we will forget about the origial copy of |collect|.
+ // If we are retaining it, we will treat it as part of |keep2|.
+ // For clarity we will define |keep3| = |collect|keep2| or |keep2|.
+
+ BLOCK_COMMENT("adjust trailing arguments {");
+ // Compare the sizes of |+dest+| and |-collect-|, which are opposed opening and closing movements.
+ int open_count = dest_count;
+ RegisterOrConstant close_count = collect_count_constant;
+ Register rdi_close_count = rdi_collect_count;
+ if (retain_original_args) {
+ close_count = constant(0);
+ } else if (collect_count_constant == -1) {
+ close_count = rdi_collect_count;
+ }
+
+ // How many slots need moving? This is simply dest_slot (0 => no |keep3|).
+ RegisterOrConstant keep3_count;
+ Register rsi_keep3_count = rsi; // can repair from RF.exact_sender_sp
+ if (dest_slot_constant >= 0) {
+ keep3_count = dest_slot_constant;
+ } else {
+ load_conversion_vminfo(_masm, rsi_keep3_count, conversion_addr);
+ keep3_count = rsi_keep3_count;
+ }
+#ifdef ASSERT
+ if (VerifyMethodHandles && dest_slot_constant >= 0) {
+ load_conversion_vminfo(_masm, rbx_temp, conversion_addr);
+ Label L_vminfo_ok;
+ __ cmpl(rbx_temp, dest_slot_constant);
+ __ jcc(Assembler::equal, L_vminfo_ok);
+ __ stop("bad vminfo in AMH.conv");
+ __ BIND(L_vminfo_ok);
+ }
+#endif //ASSERT
+
+ // tasks remaining:
+ bool move_keep3 = (!keep3_count.is_constant() || keep3_count.as_constant() != 0);
+ bool stomp_dest = (NOT_DEBUG(dest == T_OBJECT) DEBUG_ONLY(dest_count != 0));
+ bool fix_arg_base = (!close_count.is_constant() || open_count != close_count.as_constant());
+
+ if (stomp_dest | fix_arg_base) {
+ // we will probably need an updated rax_argv value
+ if (collect_slot_constant >= 0) {
+ // rax_coll already holds the leading edge of |keep2|, so tweak it
+ assert(rax_coll == rax_argv, "elided a move");
+ if (collect_slot_constant != 0)
+ __ subptr(rax_argv, collect_slot_constant * Interpreter::stackElementSize);
+ } else {
+ // Just reload from RF.saved_args_base.
+ __ movptr(rax_argv, saved_args_base_addr);
+ }
+ }
+
+ // Old and new argument locations (based at slot 0).
+ // Net shift (&new_argv - &old_argv) is (close_count - open_count).
+ bool zero_open_count = (open_count == 0); // remember this bit of info
+ if (move_keep3 && fix_arg_base) {
+ // It will be easier t have everything in one register:
+ if (close_count.is_register()) {
+ // Deduct open_count from close_count register to get a clean +/- value.
+ __ subptr(close_count.as_register(), open_count);
+ } else {
+ close_count = close_count.as_constant() - open_count;
+ }
+ open_count = 0;
+ }
+ Address old_argv(rax_argv, 0);
+ Address new_argv(rax_argv, close_count, Interpreter::stackElementScale(),
+ - open_count * Interpreter::stackElementSize);
+
+ // First decide if any actual data are to be moved.
+ // We can skip if (a) |keep3| is empty, or (b) the argument list size didn't change.
+ // (As it happens, all movements involve an argument list size change.)
+
+ // If there are variable parameters, use dynamic checks to skip around the whole mess.
+ Label L_done;
+ if (!keep3_count.is_constant()) {
+ __ testl(keep3_count.as_register(), keep3_count.as_register());
+ __ jcc(Assembler::zero, L_done);
+ }
+ if (!close_count.is_constant()) {
+ __ cmpl(close_count.as_register(), open_count);
+ __ jcc(Assembler::equal, L_done);
+ }
+
+ if (move_keep3 && fix_arg_base) {
+ bool emit_move_down = false, emit_move_up = false, emit_guard = false;
+ if (!close_count.is_constant()) {
+ emit_move_down = emit_guard = !zero_open_count;
+ emit_move_up = true;
+ } else if (open_count != close_count.as_constant()) {
+ emit_move_down = (open_count > close_count.as_constant());
+ emit_move_up = !emit_move_down;
+ }
+ Label L_move_up;
+ if (emit_guard) {
+ __ cmpl(close_count.as_register(), open_count);
+ __ jcc(Assembler::greater, L_move_up);
+ }
+
+ if (emit_move_down) {
+ // Move arguments down if |+dest+| > |-collect-|
+ // (This is rare, except when arguments are retained.)
+ // This opens space for the return value.
+ if (keep3_count.is_constant()) {
+ for (int i = 0; i < keep3_count.as_constant(); i++) {
+ __ movptr(rdx_temp, old_argv.plus_disp(i * Interpreter::stackElementSize));
+ __ movptr( new_argv.plus_disp(i * Interpreter::stackElementSize), rdx_temp);
+ }
+ } else {
+ Register rbx_argv_top = rbx_temp;
+ __ lea(rbx_argv_top, old_argv.plus_disp(keep3_count, Interpreter::stackElementScale()));
+ move_arg_slots_down(_masm,
+ old_argv, // beginning of old argv
+ rbx_argv_top, // end of old argv
+ close_count, // distance to move down (must be negative)
+ rax_argv, rdx_temp);
+ // Used argv as an iteration variable; reload from RF.saved_args_base.
+ __ movptr(rax_argv, saved_args_base_addr);
+ }
+ }
+
+ if (emit_guard) {
+ __ jmp(L_done); // assumes emit_move_up is true also
+ __ BIND(L_move_up);
+ }
+
+ if (emit_move_up) {
+
+ // Move arguments up if |+dest+| < |-collect-|
+ // (This is usual, except when |keep3| is empty.)
+ // This closes up the space occupied by the now-deleted collect values.
+ if (keep3_count.is_constant()) {
+ for (int i = keep3_count.as_constant() - 1; i >= 0; i--) {
+ __ movptr(rdx_temp, old_argv.plus_disp(i * Interpreter::stackElementSize));
+ __ movptr( new_argv.plus_disp(i * Interpreter::stackElementSize), rdx_temp);
+ }
+ } else {
+ Address argv_top = old_argv.plus_disp(keep3_count, Interpreter::stackElementScale());
+ move_arg_slots_up(_masm,
+ rax_argv, // beginning of old argv
+ argv_top, // end of old argv
+ close_count, // distance to move up (must be positive)
+ rbx_temp, rdx_temp);
+ }
+ }
+ }
+ __ BIND(L_done);
+
+ if (fix_arg_base) {
+ // adjust RF.saved_args_base by adding (close_count - open_count)
+ if (!new_argv.is_same_address(Address(rax_argv, 0)))
+ __ lea(rax_argv, new_argv);
+ __ movptr(saved_args_base_addr, rax_argv);
+ }
+
+ if (stomp_dest) {
+ // Stomp the return slot, so it doesn't hold garbage.
+ // This isn't strictly necessary, but it may help detect bugs.
+ int forty_two = RicochetFrame::RETURN_VALUE_PLACEHOLDER;
+ __ movptr(Address(rax_argv, keep3_count, Address::times_ptr),
+ (int32_t) forty_two);
+ // uses rsi_keep3_count
+ }
+ BLOCK_COMMENT("} adjust trailing arguments");
+
+ BLOCK_COMMENT("do_recursive_call");
+ __ mov(saved_last_sp, rsp); // set rsi/r13 for callee
+ __ pushptr(ExternalAddress(SharedRuntime::ricochet_blob()->bounce_addr()).addr());
+ // The globally unique bounce address has two purposes:
+ // 1. It helps the JVM recognize this frame (frame::is_ricochet_frame).
+ // 2. When returned to, it cuts back the stack and redirects control flow
+ // to the return handler.
+ // The return handler will further cut back the stack when it takes
+ // down the RF. Perhaps there is a way to streamline this further.
+
+ // State during recursive call:
+ // ... keep1 | dest | dest=42 | keep3 | RF... | collect | bounce_pc |
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+
+ break;
+ }
+
+ case _adapter_opt_return_ref:
+ case _adapter_opt_return_int:
+ case _adapter_opt_return_long:
+ case _adapter_opt_return_float:
+ case _adapter_opt_return_double:
+ case _adapter_opt_return_void:
+ case _adapter_opt_return_S0_ref:
+ case _adapter_opt_return_S1_ref:
+ case _adapter_opt_return_S2_ref:
+ case _adapter_opt_return_S3_ref:
+ case _adapter_opt_return_S4_ref:
+ case _adapter_opt_return_S5_ref:
+ {
+ BasicType dest_type_constant = ek_adapter_opt_return_type(ek);
+ int dest_slot_constant = ek_adapter_opt_return_slot(ek);
+
+ if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm);
+
+ if (dest_slot_constant == -1) {
+ // The current stub is a general handler for this dest_type.
+ // It can be called from _adapter_opt_return_any below.
+ // Stash the address in a little table.
+ assert((dest_type_constant & CONV_TYPE_MASK) == dest_type_constant, "oob");
+ address return_handler = __ pc();
+ _adapter_return_handlers[dest_type_constant] = return_handler;
+ if (dest_type_constant == T_INT) {
+ // do the subword types too
+ for (int bt = T_BOOLEAN; bt < T_INT; bt++) {
+ if (is_subword_type(BasicType(bt)) &&
+ _adapter_return_handlers[bt] == NULL) {
+ _adapter_return_handlers[bt] = return_handler;
+ }
+ }
+ }
+ }
+
+ Register rbx_arg_base = rbx_temp;
+ assert_different_registers(rax, rdx, // possibly live return value registers
+ rdi_temp, rbx_arg_base);
+
+ Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes());
+ Address saved_args_base_addr = RicochetFrame::frame_address(RicochetFrame::saved_args_base_offset_in_bytes());
+
+ __ movptr(rbx_arg_base, saved_args_base_addr);
+ RegisterOrConstant dest_slot = dest_slot_constant;
+ if (dest_slot_constant == -1) {
+ load_conversion_vminfo(_masm, rdi_temp, conversion_addr);
+ dest_slot = rdi_temp;
+ }
+ // Store the result back into the argslot.
+ // This code uses the interpreter calling sequence, in which the return value
+ // is usually left in the TOS register, as defined by InterpreterMacroAssembler::pop.
+ // There are certain irregularities with floating point values, which can be seen
+ // in TemplateInterpreterGenerator::generate_return_entry_for.
+ move_return_value(_masm, dest_type_constant, Address(rbx_arg_base, dest_slot, Interpreter::stackElementScale()));
+
+ RicochetFrame::leave_ricochet_frame(_masm, rcx_recv, rbx_arg_base, rdx_temp);
+ __ push(rdx_temp); // repush the return PC
+
+ // Load the final target and go.
+ if (VerifyMethodHandles) verify_method_handle(_masm, rcx_recv);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ __ hlt(); // --------------------
+ break;
+ }
+
+ case _adapter_opt_return_any:
+ {
+ if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm);
+ Register rdi_conv = rdi_temp;
+ assert_different_registers(rax, rdx, // possibly live return value registers
+ rdi_conv, rbx_temp);
+
+ Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes());
+ load_conversion_dest_type(_masm, rdi_conv, conversion_addr);
+ __ lea(rbx_temp, ExternalAddress((address) &_adapter_return_handlers[0]));
+ __ movptr(rbx_temp, Address(rbx_temp, rdi_conv, Address::times_ptr));
+
+#ifdef ASSERT
+ { Label L_badconv;
+ __ testptr(rbx_temp, rbx_temp);
+ __ jccb(Assembler::zero, L_badconv);
+ __ jmp(rbx_temp);
+ __ bind(L_badconv);
+ __ stop("bad method handle return");
+ }
+#else //ASSERT
+ __ jmp(rbx_temp);
+#endif //ASSERT
+ break;
+ }
+
case _adapter_opt_spread_0:
- case _adapter_opt_spread_1:
- case _adapter_opt_spread_more:
+ case _adapter_opt_spread_1_ref:
+ case _adapter_opt_spread_2_ref:
+ case _adapter_opt_spread_3_ref:
+ case _adapter_opt_spread_4_ref:
+ case _adapter_opt_spread_5_ref:
+ case _adapter_opt_spread_ref:
+ case _adapter_opt_spread_byte:
+ case _adapter_opt_spread_char:
+ case _adapter_opt_spread_short:
+ case _adapter_opt_spread_int:
+ case _adapter_opt_spread_long:
+ case _adapter_opt_spread_float:
+ case _adapter_opt_spread_double:
{
// spread an array out into a group of arguments
- int length_constant = get_ek_adapter_opt_spread_info(ek);
+ int length_constant = ek_adapter_opt_spread_count(ek);
+ bool length_can_be_zero = (length_constant == 0);
+ if (length_constant < 0) {
+ // some adapters with variable length must handle the zero case
+ if (!OptimizeMethodHandles ||
+ ek_adapter_opt_spread_type(ek) != T_OBJECT)
+ length_can_be_zero = true;
+ }
// 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); }
+ // grab another temp
+ Register rsi_temp = rsi;
+ { if (rsi_temp == saved_last_sp) __ push(saved_last_sp); }
+ // (preceding push must be done after argslot address is taken!)
+#define UNPUSH_RSI \
+ { if (rsi_temp == saved_last_sp) __ pop(saved_last_sp); }
// 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 rsi_array = rsi_temp;
Register rdx_array_klass = rdx_temp;
- BasicType elem_type = T_OBJECT;
+ BasicType elem_type = ek_adapter_opt_spread_type(ek);
+ int elem_slots = type2size[elem_type]; // 1 or 2
+ int array_slots = 1; // array is always a 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) {
+
+ Label L_array_is_empty, L_insert_arg_space, L_copy_args, L_args_done;
+ if (length_can_be_zero) {
+ // handle the null pointer case, if zero is allowed
+ Label L_skip;
+ if (length_constant < 0) {
+ load_conversion_vminfo(_masm, rbx_temp, rcx_amh_conversion);
+ __ testl(rbx_temp, rbx_temp);
+ __ jcc(Assembler::notZero, L_skip);
+ }
__ testptr(rsi_array, rsi_array);
- __ jcc(Assembler::zero, skip_array_check);
+ __ jcc(Assembler::zero, L_array_is_empty);
+ __ bind(L_skip);
}
__ null_check(rsi_array, oopDesc::klass_offset_in_bytes());
__ load_klass(rdx_array_klass, rsi_array);
@@ -1081,22 +2272,20 @@
// Check the array type.
Register rbx_klass = rbx_temp;
__ load_heap_oop(rbx_klass, rcx_amh_argument); // this is a Class object!
- __ load_heap_oop(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes()));
+ load_klass_from_Class(_masm, rbx_klass);
Label ok_array_klass, bad_array_klass, bad_array_length;
- __ check_klass_subtype(rdx_array_klass, rbx_klass, rdi, ok_array_klass);
+ __ check_klass_subtype(rdx_array_klass, rbx_klass, rdi_temp, ok_array_klass);
// If we get here, the type check failed!
__ jmp(bad_array_klass);
- __ bind(ok_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);
+ load_conversion_vminfo(_masm, rbx_vminfo, rcx_amh_conversion);
__ cmpl(rbx_vminfo, Address(rsi_array, length_offset));
}
__ jcc(Assembler::notEqual, bad_array_length);
@@ -1108,90 +2297,104 @@
// 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;
- __ movl2ptr(rdi_stack_move, rcx_amh_conversion);
- __ sarptr(rdi_stack_move, CONV_STACK_MOVE_SHIFT);
+ // This number already accounts for elem_slots.
+ Register rdi_stack_move = rdi_temp;
+ load_stack_move(_masm, rdi_stack_move, rcx_recv, true);
+ __ cmpptr(rdi_stack_move, 0);
+ assert(stack_move_unit() < 0, "else change this comparison");
+ __ jcc(Assembler::less, L_insert_arg_space);
+ __ jcc(Assembler::equal, L_copy_args);
+ // single argument case, with no array movement
+ __ BIND(L_array_is_empty);
+ remove_arg_slots(_masm, -stack_move_unit() * array_slots,
+ rax_argslot, rbx_temp, rdx_temp);
+ __ jmp(L_args_done); // no spreading to do
+ __ BIND(L_insert_arg_space);
+ // come here in the usual case, stack_move < 0 (2 or more spread arguments)
Register rsi_temp = rsi_array; // spill this
- insert_arg_slots(_masm, rdi_stack_move, -1,
+ insert_arg_slots(_masm, rdi_stack_move,
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,
+ // reload the array since rsi was killed
+ // reload from rdx_argslot_limit since rax_argslot is now decremented
+ __ movptr(rsi_array, Address(rdx_argslot_limit, -Interpreter::stackElementSize));
+ } else if (length_constant >= 1) {
+ int new_slots = (length_constant * elem_slots) - array_slots;
+ insert_arg_slots(_masm, new_slots * stack_move_unit(),
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(),
+ __ BIND(L_array_is_empty);
+ remove_arg_slots(_masm, -stack_move_unit() * array_slots,
rax_argslot, rbx_temp, rdx_temp);
+ } else {
+ ShouldNotReachHere();
}
// 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.
+ // Beware: Arguments that are shallow on the stack are deep in the array,
+ // and vice versa. So a downward-growing stack (the usual) has to be copied
+ // elementwise in reverse order from the source array.
+ __ BIND(L_copy_args);
if (length_constant == -1) {
// [rax_argslot, rdx_argslot_limit) is the area we are inserting into.
+ // Array element [0] goes at rdx_argslot_limit[-wordSize].
Register rsi_source = rsi_array;
__ lea(rsi_source, Address(rsi_array, elem0_offset));
+ Register rdx_fill_ptr = rdx_argslot_limit;
Label loop;
- __ bind(loop);
- __ movptr(rbx_temp, Address(rsi_source, 0));
- __ movptr(Address(rax_argslot, 0), rbx_temp);
+ __ BIND(loop);
+ __ addptr(rdx_fill_ptr, -Interpreter::stackElementSize * elem_slots);
+ move_typed_arg(_masm, elem_type, true,
+ Address(rdx_fill_ptr, 0), Address(rsi_source, 0),
+ rbx_temp, rdi_temp);
__ addptr(rsi_source, type2aelembytes(elem_type));
- __ addptr(rax_argslot, Interpreter::stackElementSize);
- __ cmpptr(rax_argslot, rdx_argslot_limit);
- __ jccb(Assembler::less, loop);
+ __ cmpptr(rdx_fill_ptr, rax_argslot);
+ __ jcc(Assembler::above, loop);
} else if (length_constant == 0) {
- __ bind(skip_array_check);
// nothing to copy
} else {
int elem_offset = elem0_offset;
- int slot_offset = 0;
+ int slot_offset = length_constant * Interpreter::stackElementSize;
for (int index = 0; index < length_constant; index++) {
- __ movptr(rbx_temp, Address(rsi_array, elem_offset));
- __ movptr(Address(rax_argslot, slot_offset), rbx_temp);
+ slot_offset -= Interpreter::stackElementSize * elem_slots; // fill backward
+ move_typed_arg(_masm, elem_type, true,
+ Address(rax_argslot, slot_offset), Address(rsi_array, elem_offset),
+ rbx_temp, rdi_temp);
elem_offset += type2aelembytes(elem_type);
- slot_offset += Interpreter::stackElementSize;
}
}
+ __ BIND(L_args_done);
// Arguments are spread. Move to next method handle.
- UNPUSH_RSI_RDI;
+ UNPUSH_RSI;
__ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
__ jump_to_method_handle_entry(rcx_recv, rdx_temp);
__ bind(bad_array_klass);
- UNPUSH_RSI_RDI;
+ UNPUSH_RSI;
assert(!vmarg.uses(rarg2_required), "must be different registers");
- __ movptr(rarg2_required, Address(rdx_array_klass, java_mirror_offset)); // required type
- __ movptr(rarg1_actual, vmarg); // bad array
- __ movl( rarg0_code, (int) Bytecodes::_aaload); // who is complaining?
+ __ load_heap_oop( rarg2_required, Address(rdx_array_klass, java_mirror_offset)); // required type
+ __ movptr( rarg1_actual, vmarg); // bad array
+ __ movl( rarg0_code, (int) Bytecodes::_aaload); // who is complaining?
__ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
__ bind(bad_array_length);
- UNPUSH_RSI_RDI;
+ UNPUSH_RSI;
assert(!vmarg.uses(rarg2_required), "must be different registers");
- __ mov (rarg2_required, rcx_recv); // AMH requiring a certain length
- __ movptr(rarg1_actual, vmarg); // bad array
- __ movl( rarg0_code, (int) Bytecodes::_arraylength); // who is complaining?
+ __ mov( rarg2_required, rcx_recv); // AMH requiring a certain length
+ __ movptr( rarg1_actual, vmarg); // bad array
+ __ movl( rarg0_code, (int) Bytecodes::_arraylength); // who is complaining?
__ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
+#undef UNPUSH_RSI
-#undef UNPUSH_RSI_RDI
+ break;
}
- break;
- case _adapter_flyby:
- case _adapter_ricochet:
- __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
- break;
-
- default: ShouldNotReachHere();
+ default:
+ // do not require all platforms to recognize all adapter types
+ __ nop();
+ return;
}
__ hlt();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+// Platform-specific definitions for method handles.
+// These definitions are inlined into class MethodHandles.
+
+public:
+
+// The stack just after the recursive call from a ricochet frame
+// looks something like this. Offsets are marked in words, not bytes.
+// rsi (r13 on LP64) is part of the interpreter calling sequence
+// which tells the callee where is my real rsp (for frame walking).
+// (...lower memory addresses)
+// rsp: [ return pc ] always the global RicochetBlob::bounce_addr
+// rsp+1: [ recursive arg N ]
+// rsp+2: [ recursive arg N-1 ]
+// ...
+// rsp+N: [ recursive arg 1 ]
+// rsp+N+1: [ recursive method handle ]
+// ...
+// rbp-6: [ cleanup continuation pc ] <-- (struct RicochetFrame)
+// rbp-5: [ saved target MH ] the MH we will call on the saved args
+// rbp-4: [ saved args layout oop ] an int[] array which describes argument layout
+// rbp-3: [ saved args pointer ] address of transformed adapter arg M (slot 0)
+// rbp-2: [ conversion ] information about how the return value is used
+// rbp-1: [ exact sender sp ] exact TOS (rsi/r13) of original sender frame
+// rbp+0: [ saved sender fp ] (for original sender of AMH)
+// rbp+1: [ saved sender pc ] (back to original sender of AMH)
+// rbp+2: [ transformed adapter arg M ] <-- (extended TOS of original sender)
+// rbp+3: [ transformed adapter arg M-1]
+// ...
+// rbp+M+1: [ transformed adapter arg 1 ]
+// rbp+M+2: [ padding ] <-- (rbp + saved args base offset)
+// ... [ optional padding]
+// (higher memory addresses...)
+//
+// The arguments originally passed by the original sender
+// are lost, and arbitrary amounts of stack motion might have
+// happened due to argument transformation.
+// (This is done by C2I/I2C adapters and non-direct method handles.)
+// This is why there is an unpredictable amount of memory between
+// the extended and exact TOS of the sender.
+// The ricochet adapter itself will also (in general) perform
+// transformations before the recursive call.
+//
+// The transformed and saved arguments, immediately above the saved
+// return PC, are a well-formed method handle invocation ready to execute.
+// When the GC needs to walk the stack, these arguments are described
+// via the saved arg types oop, an int[] array with a private format.
+// This array is derived from the type of the transformed adapter
+// method handle, which also sits at the base of the saved argument
+// bundle. Since the GC may not be able to fish out the int[]
+// array, so it is pushed explicitly on the stack. This may be
+// an unnecessary expense.
+//
+// The following register conventions are significant at this point:
+// rsp the thread stack, as always; preserved by caller
+// rsi/r13 exact TOS of recursive frame (contents of [rbp-2])
+// rcx recursive method handle (contents of [rsp+N+1])
+// rbp preserved by caller (not used by caller)
+// Unless otherwise specified, all registers can be blown by the call.
+//
+// If this frame must be walked, the transformed adapter arguments
+// will be found with the help of the saved arguments descriptor.
+//
+// Therefore, the descriptor must match the referenced arguments.
+// The arguments must be followed by at least one word of padding,
+// which will be necessary to complete the final method handle call.
+// That word is not treated as holding an oop. Neither is the word
+//
+// The word pointed to by the return argument pointer is not
+// treated as an oop, even if points to a saved argument.
+// This allows the saved argument list to have a "hole" in it
+// to receive an oop from the recursive call.
+// (The hole might temporarily contain RETURN_VALUE_PLACEHOLDER.)
+//
+// When the recursive callee returns, RicochetBlob::bounce_addr will
+// immediately jump to the continuation stored in the RF.
+// This continuation will merge the recursive return value
+// into the saved argument list. At that point, the original
+// rsi, rbp, and rsp will be reloaded, the ricochet frame will
+// disappear, and the final target of the adapter method handle
+// will be invoked on the transformed argument list.
+
+class RicochetFrame {
+ friend class MethodHandles;
+
+ private:
+ intptr_t* _continuation; // what to do when control gets back here
+ oopDesc* _saved_target; // target method handle to invoke on saved_args
+ oopDesc* _saved_args_layout; // caching point for MethodTypeForm.vmlayout cookie
+ intptr_t* _saved_args_base; // base of pushed arguments (slot 0, arg N) (-3)
+ intptr_t _conversion; // misc. information from original AdapterMethodHandle (-2)
+ intptr_t* _exact_sender_sp; // parallel to interpreter_frame_sender_sp (-1)
+ intptr_t* _sender_link; // *must* coincide with frame::link_offset (0)
+ address _sender_pc; // *must* coincide with frame::return_addr_offset (1)
+
+ public:
+ intptr_t* continuation() const { return _continuation; }
+ oop saved_target() const { return _saved_target; }
+ oop saved_args_layout() const { return _saved_args_layout; }
+ intptr_t* saved_args_base() const { return _saved_args_base; }
+ intptr_t conversion() const { return _conversion; }
+ intptr_t* exact_sender_sp() const { return _exact_sender_sp; }
+ intptr_t* sender_link() const { return _sender_link; }
+ address sender_pc() const { return _sender_pc; }
+
+ intptr_t* extended_sender_sp() const { return saved_args_base(); }
+
+ intptr_t return_value_slot_number() const {
+ return adapter_conversion_vminfo(conversion());
+ }
+ BasicType return_value_type() const {
+ return adapter_conversion_dest_type(conversion());
+ }
+ bool has_return_value_slot() const {
+ return return_value_type() != T_VOID;
+ }
+ intptr_t* return_value_slot_addr() const {
+ assert(has_return_value_slot(), "");
+ return saved_arg_slot_addr(return_value_slot_number());
+ }
+ intptr_t* saved_target_slot_addr() const {
+ return saved_arg_slot_addr(saved_args_length());
+ }
+ intptr_t* saved_arg_slot_addr(int slot) const {
+ assert(slot >= 0, "");
+ return (intptr_t*)( (address)saved_args_base() + (slot * Interpreter::stackElementSize) );
+ }
+
+ jint saved_args_length() const;
+ jint saved_arg_offset(int arg) const;
+
+ // GC interface
+ oop* saved_target_addr() { return (oop*)&_saved_target; }
+ oop* saved_args_layout_addr() { return (oop*)&_saved_args_layout; }
+
+ oop compute_saved_args_layout(bool read_cache, bool write_cache);
+
+ // Compiler/assembler interface.
+ static int continuation_offset_in_bytes() { return offset_of(RicochetFrame, _continuation); }
+ static int saved_target_offset_in_bytes() { return offset_of(RicochetFrame, _saved_target); }
+ static int saved_args_layout_offset_in_bytes(){ return offset_of(RicochetFrame, _saved_args_layout); }
+ static int saved_args_base_offset_in_bytes() { return offset_of(RicochetFrame, _saved_args_base); }
+ static int conversion_offset_in_bytes() { return offset_of(RicochetFrame, _conversion); }
+ static int exact_sender_sp_offset_in_bytes() { return offset_of(RicochetFrame, _exact_sender_sp); }
+ static int sender_link_offset_in_bytes() { return offset_of(RicochetFrame, _sender_link); }
+ static int sender_pc_offset_in_bytes() { return offset_of(RicochetFrame, _sender_pc); }
+
+ // This value is not used for much, but it apparently must be nonzero.
+ static int frame_size_in_bytes() { return sender_link_offset_in_bytes(); }
+
+#ifdef ASSERT
+ // The magic number is supposed to help find ricochet frames within the bytes of stack dumps.
+ enum { MAGIC_NUMBER_1 = 0xFEED03E, MAGIC_NUMBER_2 = 0xBEEF03E };
+ static int magic_number_1_offset_in_bytes() { return -wordSize; }
+ static int magic_number_2_offset_in_bytes() { return sizeof(RicochetFrame); }
+ intptr_t magic_number_1() const { return *(intptr_t*)((address)this + magic_number_1_offset_in_bytes()); };
+ intptr_t magic_number_2() const { return *(intptr_t*)((address)this + magic_number_2_offset_in_bytes()); };
+#endif //ASSERT
+
+ enum { RETURN_VALUE_PLACEHOLDER = (NOT_DEBUG(0) DEBUG_ONLY(42)) };
+
+ static void verify_offsets() NOT_DEBUG_RETURN;
+ void verify() const NOT_DEBUG_RETURN; // check for MAGIC_NUMBER, etc.
+ void zap_arguments() NOT_DEBUG_RETURN;
+
+ static void generate_ricochet_blob(MacroAssembler* _masm,
+ // output params:
+ int* frame_size_in_words, int* bounce_offset, int* exception_offset);
+
+ static void enter_ricochet_frame(MacroAssembler* _masm,
+ Register rcx_recv,
+ Register rax_argv,
+ address return_handler,
+ Register rbx_temp);
+ static void leave_ricochet_frame(MacroAssembler* _masm,
+ Register rcx_recv,
+ Register new_sp_reg,
+ Register sender_pc_reg);
+
+ static Address frame_address(int offset = 0) {
+ // The RicochetFrame is found by subtracting a constant offset from rbp.
+ return Address(rbp, - sender_link_offset_in_bytes() + offset);
+ }
+
+ static RicochetFrame* from_frame(const frame& fr) {
+ address bp = (address) fr.fp();
+ RicochetFrame* rf = (RicochetFrame*)(bp - sender_link_offset_in_bytes());
+ rf->verify();
+ return rf;
+ }
+
+ static void verify_clean(MacroAssembler* _masm) NOT_DEBUG_RETURN;
+};
+
+// Additional helper methods for MethodHandles code generation:
+public:
+ static void load_klass_from_Class(MacroAssembler* _masm, Register klass_reg);
+ static void load_conversion_vminfo(MacroAssembler* _masm, Register reg, Address conversion_field_addr);
+ static void load_conversion_dest_type(MacroAssembler* _masm, Register reg, Address conversion_field_addr);
+
+ static void load_stack_move(MacroAssembler* _masm,
+ Register rdi_stack_move,
+ Register rcx_amh,
+ bool might_be_negative);
+
+ static void insert_arg_slots(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ 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);
+
+ static void push_arg_slots(MacroAssembler* _masm,
+ Register rax_argslot,
+ RegisterOrConstant slot_count,
+ int skip_words_count,
+ Register rbx_temp, Register rdx_temp);
+
+ static void move_arg_slots_up(MacroAssembler* _masm,
+ Register rbx_bottom, // invariant
+ Address top_addr, // can use rax_temp
+ RegisterOrConstant positive_distance_in_slots,
+ Register rax_temp, Register rdx_temp);
+
+ static void move_arg_slots_down(MacroAssembler* _masm,
+ Address bottom_addr, // can use rax_temp
+ Register rbx_top, // invariant
+ RegisterOrConstant negative_distance_in_slots,
+ Register rax_temp, Register rdx_temp);
+
+ static void move_typed_arg(MacroAssembler* _masm,
+ BasicType type, bool is_element,
+ Address slot_dest, Address value_src,
+ Register rbx_temp, Register rdx_temp);
+
+ static void move_return_value(MacroAssembler* _masm, BasicType type,
+ Address return_slot);
+
+ static void verify_argslot(MacroAssembler* _masm, Register argslot_reg,
+ const char* error_message) NOT_DEBUG_RETURN;
+
+ static void verify_argslots(MacroAssembler* _masm,
+ RegisterOrConstant argslot_count,
+ Register argslot_reg,
+ bool negate_argslot,
+ const char* error_message) NOT_DEBUG_RETURN;
+
+ static void verify_stack_move(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ int direction) NOT_DEBUG_RETURN;
+
+ static void verify_klass(MacroAssembler* _masm,
+ Register obj, KlassHandle klass,
+ const char* error_message = "wrong klass") NOT_DEBUG_RETURN;
+
+ static void verify_method_handle(MacroAssembler* _masm, Register mh_reg) {
+ verify_klass(_masm, mh_reg, SystemDictionaryHandles::MethodHandle_klass(),
+ "reference is a MH");
+ }
+
+ static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
+
+ static Register saved_last_sp_register() {
+ // Should be in sharedRuntime, not here.
+ return LP64_ONLY(r13) NOT_LP64(rsi);
+ }
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Tue May 24 14:32:10 2011 -0700
@@ -2253,6 +2253,31 @@
return 0;
}
+//----------------------------generate_ricochet_blob---------------------------
+void SharedRuntime::generate_ricochet_blob() {
+ if (!EnableInvokeDynamic) return; // leave it as a null
+
+ // allocate space for the code
+ ResourceMark rm;
+ // setup code generation tools
+ CodeBuffer buffer("ricochet_blob", 256, 256);
+ MacroAssembler* masm = new MacroAssembler(&buffer);
+
+ int frame_size_in_words = -1, bounce_offset = -1, exception_offset = -1;
+ MethodHandles::RicochetFrame::generate_ricochet_blob(masm, &frame_size_in_words, &bounce_offset, &exception_offset);
+
+ // -------------
+ // make sure all code is generated
+ masm->flush();
+
+ // failed to generate?
+ if (frame_size_in_words < 0 || bounce_offset < 0 || exception_offset < 0) {
+ assert(false, "bad ricochet blob");
+ return;
+ }
+
+ _ricochet_blob = RicochetBlob::create(&buffer, bounce_offset, exception_offset, frame_size_in_words);
+}
//------------------------------generate_deopt_blob----------------------------
void SharedRuntime::generate_deopt_blob() {
@@ -2996,6 +3021,8 @@
generate_handler_blob(CAST_FROM_FN_PTR(address,
SafepointSynchronize::handle_polling_page_exception), true);
+ generate_ricochet_blob();
+
generate_deopt_blob();
#ifdef COMPILER2
generate_uncommon_trap_blob();
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Tue May 24 14:32:10 2011 -0700
@@ -2530,6 +2530,32 @@
}
+//----------------------------generate_ricochet_blob---------------------------
+void SharedRuntime::generate_ricochet_blob() {
+ if (!EnableInvokeDynamic) return; // leave it as a null
+
+ // allocate space for the code
+ ResourceMark rm;
+ // setup code generation tools
+ CodeBuffer buffer("ricochet_blob", 512, 512);
+ MacroAssembler* masm = new MacroAssembler(&buffer);
+
+ int frame_size_in_words = -1, bounce_offset = -1, exception_offset = -1;
+ MethodHandles::RicochetFrame::generate_ricochet_blob(masm, &frame_size_in_words, &bounce_offset, &exception_offset);
+
+ // -------------
+ // make sure all code is generated
+ masm->flush();
+
+ // failed to generate?
+ if (frame_size_in_words < 0 || bounce_offset < 0 || exception_offset < 0) {
+ assert(false, "bad ricochet blob");
+ return;
+ }
+
+ _ricochet_blob = RicochetBlob::create(&buffer, bounce_offset, exception_offset, frame_size_in_words);
+}
+
//------------------------------generate_deopt_blob----------------------------
void SharedRuntime::generate_deopt_blob() {
// Allocate space for the code
@@ -3205,6 +3231,8 @@
generate_handler_blob(CAST_FROM_FN_PTR(address,
SafepointSynchronize::handle_polling_page_exception), true);
+ generate_ricochet_blob();
+
generate_deopt_blob();
#ifdef COMPILER2
--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp Tue May 24 14:32:10 2011 -0700
@@ -36,7 +36,7 @@
// MethodHandles adapters
enum method_handles_platform_dependent_constants {
- method_handles_adapters_code_size = 10000
+ method_handles_adapters_code_size = 30000 DEBUG_ONLY(+ 10000)
};
class x86 {
--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp Tue May 24 14:32:10 2011 -0700
@@ -38,7 +38,7 @@
// MethodHandles adapters
enum method_handles_platform_dependent_constants {
- method_handles_adapters_code_size = 40000
+ method_handles_adapters_code_size = 80000 DEBUG_ONLY(+ 120000)
};
class x86 {
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Tue May 24 14:32:10 2011 -0700
@@ -1589,6 +1589,7 @@
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_locals,
frame* caller,
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Tue May 24 14:32:10 2011 -0700
@@ -1603,6 +1603,7 @@
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_locals,
frame* caller,
--- a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp Tue May 24 14:32:10 2011 -0700
@@ -1427,6 +1427,7 @@
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_locals,
frame* caller,
--- a/hotspot/src/cpu/zero/vm/interpreter_zero.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/cpu/zero/vm/interpreter_zero.cpp Tue May 24 14:32:10 2011 -0700
@@ -82,24 +82,6 @@
return true;
}
-int AbstractInterpreter::size_activation(methodOop method,
- int tempcount,
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- bool is_top_frame) {
- return layout_activation(method,
- tempcount,
- popframe_extra_args,
- moncount,
- callee_param_count,
- callee_locals,
- (frame*) NULL,
- (frame*) NULL,
- is_top_frame);
-}
-
void Deoptimization::unwind_callee_save_values(frame* f,
vframeArray* vframe_array) {
}
--- a/hotspot/src/os/linux/vm/os_linux.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/os/linux/vm/os_linux.cpp Tue May 24 14:32:10 2011 -0700
@@ -2850,7 +2850,7 @@
char chars[257];
long x = 0;
if (fgets(chars, sizeof(chars), fp)) {
- if (sscanf(chars, "%lx-%*lx", &x) == 1
+ if (sscanf(chars, "%lx-%*x", &x) == 1
&& x == (long)p) {
if (strstr (chars, "hugepage")) {
result = true;
--- a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp Tue May 24 14:32:10 2011 -0700
@@ -132,17 +132,22 @@
if (value->is_null_object()) {
output()->print("null");
} else if (!value->is_loaded()) {
- output()->print("<unloaded object 0x%x>", value);
+ output()->print("<unloaded object " PTR_FORMAT ">", value);
} else if (value->is_method()) {
ciMethod* m = (ciMethod*)value;
output()->print("<method %s.%s>", m->holder()->name()->as_utf8(), m->name()->as_utf8());
} else {
- output()->print("<object 0x%x>", value->constant_encoding());
+ output()->print("<object " PTR_FORMAT ">", value->constant_encoding());
}
} else if (type->as_InstanceConstant() != NULL) {
- output()->print("<instance 0x%x>", type->as_InstanceConstant()->value()->constant_encoding());
+ ciInstance* value = type->as_InstanceConstant()->value();
+ if (value->is_loaded()) {
+ output()->print("<instance " PTR_FORMAT ">", value->constant_encoding());
+ } else {
+ output()->print("<unloaded instance " PTR_FORMAT ">", value);
+ }
} else if (type->as_ArrayConstant() != NULL) {
- output()->print("<array 0x%x>", type->as_ArrayConstant()->value()->constant_encoding());
+ output()->print("<array " PTR_FORMAT ">", type->as_ArrayConstant()->value()->constant_encoding());
} else if (type->as_ClassConstant() != NULL) {
ciInstanceKlass* klass = type->as_ClassConstant()->value();
if (!klass->is_loaded()) {
--- a/hotspot/src/share/vm/c1/c1_Optimizer.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/c1/c1_Optimizer.cpp Tue May 24 14:32:10 2011 -0700
@@ -252,26 +252,28 @@
Constant::CompareResult t_compare_res = x_tval_const->compare(cond, y_const);
Constant::CompareResult f_compare_res = x_fval_const->compare(cond, y_const);
- guarantee(t_compare_res != Constant::not_comparable && f_compare_res != Constant::not_comparable, "incomparable constants in IfOp");
-
- Value new_tval = t_compare_res == Constant::cond_true ? tval : fval;
- Value new_fval = f_compare_res == Constant::cond_true ? tval : fval;
+ // not_comparable here is a valid return in case we're comparing unloaded oop constants
+ if (t_compare_res != Constant::not_comparable && f_compare_res != Constant::not_comparable) {
+ Value new_tval = t_compare_res == Constant::cond_true ? tval : fval;
+ Value new_fval = f_compare_res == Constant::cond_true ? tval : fval;
- _ifop_count++;
- if (new_tval == new_fval) {
- return new_tval;
- } else {
- return new IfOp(x_ifop->x(), x_ifop_cond, x_ifop->y(), new_tval, new_fval);
+ _ifop_count++;
+ if (new_tval == new_fval) {
+ return new_tval;
+ } else {
+ return new IfOp(x_ifop->x(), x_ifop_cond, x_ifop->y(), new_tval, new_fval);
+ }
}
}
} else {
Constant* x_const = x->as_Constant();
if (x_const != NULL) { // x and y are constants
Constant::CompareResult x_compare_res = x_const->compare(cond, y_const);
- guarantee(x_compare_res != Constant::not_comparable, "incomparable constants in IfOp");
-
- _ifop_count++;
- return x_compare_res == Constant::cond_true ? tval : fval;
+ // not_comparable here is a valid return in case we're comparing unloaded oop constants
+ if (x_compare_res != Constant::not_comparable) {
+ _ifop_count++;
+ return x_compare_res == Constant::cond_true ? tval : fval;
+ }
}
}
}
--- a/hotspot/src/share/vm/ci/ciMethodData.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/ci/ciMethodData.hpp Tue May 24 14:32:10 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2011, Oracle and/or its affiliates. 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
@@ -233,7 +233,10 @@
public:
bool is_method_data() { return true; }
- bool is_empty() { return _state == empty_state; }
+
+ void set_mature() { _state = mature_state; }
+
+ bool is_empty() { return _state == empty_state; }
bool is_mature() { return _state == mature_state; }
int creation_mileage() { return _orig.creation_mileage(); }
--- a/hotspot/src/share/vm/ci/ciMethodHandle.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/ci/ciMethodHandle.cpp Tue May 24 14:32:10 2011 -0700
@@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "ci/ciClassList.hpp"
#include "ci/ciInstance.hpp"
+#include "ci/ciMethodData.hpp"
#include "ci/ciMethodHandle.hpp"
#include "ci/ciUtilities.hpp"
#include "prims/methodHandleWalk.hpp"
@@ -36,13 +37,13 @@
// ciMethodHandle::get_adapter
//
// Return an adapter for this MethodHandle.
-ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const {
+ciMethod* ciMethodHandle::get_adapter_impl(bool is_invokedynamic) const {
VM_ENTRY_MARK;
Handle h(get_oop());
methodHandle callee(_callee->get_methodOop());
// We catch all exceptions here that could happen in the method
// handle compiler and stop the VM.
- MethodHandleCompiler mhc(h, callee, is_invokedynamic, THREAD);
+ MethodHandleCompiler mhc(h, callee, _profile->count(), is_invokedynamic, THREAD);
if (!HAS_PENDING_EXCEPTION) {
methodHandle m = mhc.compile(THREAD);
if (!HAS_PENDING_EXCEPTION) {
@@ -58,6 +59,22 @@
return NULL;
}
+// ------------------------------------------------------------------
+// ciMethodHandle::get_adapter
+//
+// Return an adapter for this MethodHandle.
+ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const {
+ ciMethod* result = get_adapter_impl(is_invokedynamic);
+ if (result) {
+ // Fake up the MDO maturity.
+ ciMethodData* mdo = result->method_data();
+ if (mdo != NULL && _caller->method_data() != NULL && _caller->method_data()->is_mature()) {
+ mdo->set_mature();
+ }
+ }
+ return result;
+}
+
// ------------------------------------------------------------------
// ciMethodHandle::print_impl
--- a/hotspot/src/share/vm/ci/ciMethodHandle.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/ci/ciMethodHandle.hpp Tue May 24 14:32:10 2011 -0700
@@ -25,6 +25,7 @@
#ifndef SHARE_VM_CI_CIMETHODHANDLE_HPP
#define SHARE_VM_CI_CIMETHODHANDLE_HPP
+#include "ci/ciCallProfile.hpp"
#include "ci/ciInstance.hpp"
#include "prims/methodHandles.hpp"
@@ -33,32 +34,37 @@
// The class represents a java.lang.invoke.MethodHandle object.
class ciMethodHandle : public ciInstance {
private:
- ciMethod* _callee;
+ ciMethod* _callee;
+ ciMethod* _caller;
+ ciCallProfile* _profile;
// Return an adapter for this MethodHandle.
- ciMethod* get_adapter(bool is_invokedynamic) const;
+ ciMethod* get_adapter_impl(bool is_invokedynamic) const;
+ ciMethod* get_adapter( bool is_invokedynamic) const;
protected:
void print_impl(outputStream* st);
public:
- ciMethodHandle(instanceHandle h_i) : ciInstance(h_i) {};
+ ciMethodHandle(instanceHandle h_i) :
+ ciInstance(h_i),
+ _callee(NULL),
+ _caller(NULL),
+ _profile(NULL)
+ {}
// What kind of ciObject is this?
bool is_method_handle() const { return true; }
- ciMethod* callee() const { return _callee; }
- void set_callee(ciMethod* m) { _callee = m; }
+ void set_callee(ciMethod* m) { _callee = m; }
+ void set_caller(ciMethod* m) { _caller = m; }
+ void set_call_profile(ciCallProfile* profile) { _profile = profile; }
// Return an adapter for a MethodHandle call.
- ciMethod* get_method_handle_adapter() const {
- return get_adapter(false);
- }
+ ciMethod* get_method_handle_adapter() const { return get_adapter(false); }
// Return an adapter for an invokedynamic call.
- ciMethod* get_invokedynamic_adapter() const {
- return get_adapter(true);
- }
+ ciMethod* get_invokedynamic_adapter() const { return get_adapter(true); }
};
#endif // SHARE_VM_CI_CIMETHODHANDLE_HPP
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Tue May 24 14:32:10 2011 -0700
@@ -2602,6 +2602,7 @@
// Support for java_lang_invoke_MethodTypeForm
int java_lang_invoke_MethodTypeForm::_vmslots_offset;
+int java_lang_invoke_MethodTypeForm::_vmlayout_offset;
int java_lang_invoke_MethodTypeForm::_erasedType_offset;
int java_lang_invoke_MethodTypeForm::_genericInvoker_offset;
@@ -2609,6 +2610,7 @@
klassOop k = SystemDictionary::MethodTypeForm_klass();
if (k != NULL) {
compute_optional_offset(_vmslots_offset, k, vmSymbols::vmslots_name(), vmSymbols::int_signature(), true);
+ compute_optional_offset(_vmlayout_offset, k, vmSymbols::vmlayout_name(), vmSymbols::object_signature());
compute_optional_offset(_erasedType_offset, k, vmSymbols::erasedType_name(), vmSymbols::java_lang_invoke_MethodType_signature(), true);
compute_optional_offset(_genericInvoker_offset, k, vmSymbols::genericInvoker_name(), vmSymbols::java_lang_invoke_MethodHandle_signature(), true);
if (_genericInvoker_offset == 0) _genericInvoker_offset = -1; // set to explicit "empty" value
@@ -2617,9 +2619,31 @@
int java_lang_invoke_MethodTypeForm::vmslots(oop mtform) {
assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
+ assert(_vmslots_offset > 0, "");
return mtform->int_field(_vmslots_offset);
}
+oop java_lang_invoke_MethodTypeForm::vmlayout(oop mtform) {
+ assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
+ assert(_vmlayout_offset > 0, "");
+ return mtform->obj_field(_vmlayout_offset);
+}
+
+oop java_lang_invoke_MethodTypeForm::init_vmlayout(oop mtform, oop cookie) {
+ assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
+ oop previous = vmlayout(mtform);
+ if (previous != NULL) {
+ return previous; // someone else beat us to it
+ }
+ HeapWord* cookie_addr = (HeapWord*) mtform->obj_field_addr<oop>(_vmlayout_offset);
+ OrderAccess::storestore(); // make sure our copy is fully committed
+ previous = oopDesc::atomic_compare_exchange_oop(cookie, cookie_addr, previous);
+ if (previous != NULL) {
+ return previous; // someone else beat us to it
+ }
+ return cookie;
+}
+
oop java_lang_invoke_MethodTypeForm::erasedType(oop mtform) {
assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
return mtform->obj_field(_erasedType_offset);
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Tue May 24 14:32:10 2011 -0700
@@ -949,18 +949,19 @@
OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument
OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive
- OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI)
+ OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper
OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg)
OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
OP_DROP_ARGS = 0x9, // remove one or more argument slots
- OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI)
+ OP_COLLECT_ARGS = 0xA, // combine arguments using an auxiliary function
OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
- OP_FLYBY = 0xC, // operate first on reified argument list (NYI)
- OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI)
+ OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result
+ //OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists
CONV_OP_LIMIT = 0xE, // limit of CONV_OP enumeration
CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
+ CONV_TYPE_MASK = 0x0F, // fits T_ADDRESS and below
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
@@ -1089,6 +1090,7 @@
private:
static int _vmslots_offset; // number of argument slots needed
+ static int _vmlayout_offset; // object describing internal calling sequence
static int _erasedType_offset; // erasedType = canonical MethodType
static int _genericInvoker_offset; // genericInvoker = adapter for invokeGeneric
@@ -1100,8 +1102,12 @@
static oop erasedType(oop mtform);
static oop genericInvoker(oop mtform);
+ static oop vmlayout(oop mtform);
+ static oop init_vmlayout(oop mtform, oop cookie);
+
// Accessors for code generation:
static int vmslots_offset_in_bytes() { return _vmslots_offset; }
+ static int vmlayout_offset_in_bytes() { return _vmlayout_offset; }
static int erasedType_offset_in_bytes() { return _erasedType_offset; }
static int genericInvoker_offset_in_bytes() { return _genericInvoker_offset; }
};
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Tue May 24 14:32:10 2011 -0700
@@ -2362,8 +2362,15 @@
spe = invoke_method_table()->find_entry(index, hash, signature, name_id);
if (spe == NULL)
spe = invoke_method_table()->add_entry(index, hash, signature, name_id);
- if (spe->property_oop() == NULL)
+ if (spe->property_oop() == NULL) {
spe->set_property_oop(m());
+ // Link m to his method type, if it is suitably generic.
+ oop mtform = java_lang_invoke_MethodType::form(mt());
+ if (mtform != NULL && mt() == java_lang_invoke_MethodTypeForm::erasedType(mtform)
+ && java_lang_invoke_MethodTypeForm::vmlayout_offset_in_bytes() > 0) {
+ java_lang_invoke_MethodTypeForm::init_vmlayout(mtform, m());
+ }
+ }
} else {
non_cached_result = m;
}
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp Tue May 24 14:32:10 2011 -0700
@@ -341,6 +341,7 @@
template(vmtarget_name, "vmtarget") \
template(vmentry_name, "vmentry") \
template(vmslots_name, "vmslots") \
+ template(vmlayout_name, "vmlayout") \
template(vmindex_name, "vmindex") \
template(vmargslot_name, "vmargslot") \
template(flags_name, "flags") \
@@ -393,6 +394,7 @@
template(void_signature, "V") \
template(byte_array_signature, "[B") \
template(char_array_signature, "[C") \
+ template(int_array_signature, "[I") \
template(object_void_signature, "(Ljava/lang/Object;)V") \
template(object_int_signature, "(Ljava/lang/Object;)I") \
template(object_boolean_signature, "(Ljava/lang/Object;)Z") \
@@ -471,6 +473,13 @@
template(sun_management_ManagementFactory, "sun/management/ManagementFactory") \
template(sun_management_Sensor, "sun/management/Sensor") \
template(sun_management_Agent, "sun/management/Agent") \
+ template(sun_management_GarbageCollectorImpl, "sun/management/GarbageCollectorImpl") \
+ template(getGcInfoBuilder_name, "getGcInfoBuilder") \
+ template(getGcInfoBuilder_signature, "()Lsun/management/GcInfoBuilder;") \
+ template(com_sun_management_GcInfo, "com/sun/management/GcInfo") \
+ template(com_sun_management_GcInfo_constructor_signature, "(Lsun/management/GcInfoBuilder;JJJ[Ljava/lang/management/MemoryUsage;[Ljava/lang/management/MemoryUsage;[Ljava/lang/Object;)V") \
+ template(createGCNotification_name, "createGCNotification") \
+ template(createGCNotification_signature, "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/sun/management/GcInfo;)V") \
template(createMemoryPoolMBean_name, "createMemoryPoolMBean") \
template(createMemoryManagerMBean_name, "createMemoryManagerMBean") \
template(createGarbageCollectorMBean_name, "createGarbageCollectorMBean") \
@@ -488,6 +497,7 @@
template(java_lang_management_MemoryPoolMXBean, "java/lang/management/MemoryPoolMXBean") \
template(java_lang_management_MemoryManagerMXBean, "java/lang/management/MemoryManagerMXBean") \
template(java_lang_management_GarbageCollectorMXBean,"java/lang/management/GarbageCollectorMXBean") \
+ template(gcInfoBuilder_name, "gcInfoBuilder") \
template(createMemoryPool_name, "createMemoryPool") \
template(createMemoryManager_name, "createMemoryManager") \
template(createGarbageCollector_name, "createGarbageCollector") \
--- a/hotspot/src/share/vm/code/codeBlob.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/code/codeBlob.cpp Tue May 24 14:32:10 2011 -0700
@@ -152,6 +152,32 @@
}
+void CodeBlob::trace_new_stub(CodeBlob* stub, const char* name1, const char* name2) {
+ // Do not hold the CodeCache lock during name formatting.
+ assert(!CodeCache_lock->owned_by_self(), "release CodeCache before registering the stub");
+
+ if (stub != NULL) {
+ char stub_id[256];
+ assert(strlen(name1) + strlen(name2) < sizeof(stub_id), "");
+ jio_snprintf(stub_id, sizeof(stub_id), "%s%s", name1, name2);
+ if (PrintStubCode) {
+ tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, (intptr_t) stub);
+ Disassembler::decode(stub->code_begin(), stub->code_end());
+ }
+ Forte::register_stub(stub_id, stub->code_begin(), stub->code_end());
+
+ if (JvmtiExport::should_post_dynamic_code_generated()) {
+ const char* stub_name = name2;
+ if (name2[0] == '\0') stub_name = name1;
+ JvmtiExport::post_dynamic_code_generated(stub_name, stub->code_begin(), stub->code_end());
+ }
+ }
+
+ // Track memory usage statistic after releasing CodeCache_lock
+ MemoryService::track_code_cache_memory_usage();
+}
+
+
void CodeBlob::flush() {
if (_oop_maps) {
FREE_C_HEAP_ARRAY(unsigned char, _oop_maps);
@@ -312,23 +338,7 @@
stub = new (size) RuntimeStub(stub_name, cb, size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments);
}
- // Do not hold the CodeCache lock during name formatting.
- if (stub != NULL) {
- char stub_id[256];
- jio_snprintf(stub_id, sizeof(stub_id), "RuntimeStub - %s", stub_name);
- if (PrintStubCode) {
- tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, stub);
- Disassembler::decode(stub->code_begin(), stub->code_end());
- }
- Forte::register_stub(stub_id, stub->code_begin(), stub->code_end());
-
- if (JvmtiExport::should_post_dynamic_code_generated()) {
- JvmtiExport::post_dynamic_code_generated(stub_name, stub->code_begin(), stub->code_end());
- }
- }
-
- // Track memory usage statistic after releasing CodeCache_lock
- MemoryService::track_code_cache_memory_usage();
+ trace_new_stub(stub, "RuntimeStub - ", stub_name);
return stub;
}
@@ -340,6 +350,50 @@
return p;
}
+// operator new shared by all singletons:
+void* SingletonBlob::operator new(size_t s, unsigned size) {
+ void* p = CodeCache::allocate(size);
+ if (!p) fatal("Initial size of CodeCache is too small");
+ return p;
+}
+
+
+//----------------------------------------------------------------------------------------------------
+// Implementation of RicochetBlob
+
+RicochetBlob::RicochetBlob(
+ CodeBuffer* cb,
+ int size,
+ int bounce_offset,
+ int exception_offset,
+ int frame_size
+)
+: SingletonBlob("RicochetBlob", cb, sizeof(RicochetBlob), size, frame_size, (OopMapSet*) NULL)
+{
+ _bounce_offset = bounce_offset;
+ _exception_offset = exception_offset;
+}
+
+
+RicochetBlob* RicochetBlob::create(
+ CodeBuffer* cb,
+ int bounce_offset,
+ int exception_offset,
+ int frame_size)
+{
+ RicochetBlob* blob = NULL;
+ ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock
+ {
+ MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+ unsigned int size = allocation_size(cb, sizeof(RicochetBlob));
+ blob = new (size) RicochetBlob(cb, size, bounce_offset, exception_offset, frame_size);
+ }
+
+ trace_new_stub(blob, "RicochetBlob");
+
+ return blob;
+}
+
//----------------------------------------------------------------------------------------------------
// Implementation of DeoptimizationBlob
@@ -386,34 +440,12 @@
frame_size);
}
- // Do not hold the CodeCache lock during name formatting.
- if (blob != NULL) {
- char blob_id[256];
- jio_snprintf(blob_id, sizeof(blob_id), "DeoptimizationBlob@" PTR_FORMAT, blob->code_begin());
- if (PrintStubCode) {
- tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob);
- Disassembler::decode(blob->code_begin(), blob->code_end());
- }
- Forte::register_stub(blob_id, blob->code_begin(), blob->code_end());
-
- if (JvmtiExport::should_post_dynamic_code_generated()) {
- JvmtiExport::post_dynamic_code_generated("DeoptimizationBlob", blob->code_begin(), blob->code_end());
- }
- }
-
- // Track memory usage statistic after releasing CodeCache_lock
- MemoryService::track_code_cache_memory_usage();
+ trace_new_stub(blob, "DeoptimizationBlob");
return blob;
}
-void* DeoptimizationBlob::operator new(size_t s, unsigned size) {
- void* p = CodeCache::allocate(size);
- if (!p) fatal("Initial size of CodeCache is too small");
- return p;
-}
-
//----------------------------------------------------------------------------------------------------
// Implementation of UncommonTrapBlob
@@ -441,33 +473,12 @@
blob = new (size) UncommonTrapBlob(cb, size, oop_maps, frame_size);
}
- // Do not hold the CodeCache lock during name formatting.
- if (blob != NULL) {
- char blob_id[256];
- jio_snprintf(blob_id, sizeof(blob_id), "UncommonTrapBlob@" PTR_FORMAT, blob->code_begin());
- if (PrintStubCode) {
- tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob);
- Disassembler::decode(blob->code_begin(), blob->code_end());
- }
- Forte::register_stub(blob_id, blob->code_begin(), blob->code_end());
-
- if (JvmtiExport::should_post_dynamic_code_generated()) {
- JvmtiExport::post_dynamic_code_generated("UncommonTrapBlob", blob->code_begin(), blob->code_end());
- }
- }
-
- // Track memory usage statistic after releasing CodeCache_lock
- MemoryService::track_code_cache_memory_usage();
+ trace_new_stub(blob, "UncommonTrapBlob");
return blob;
}
-void* UncommonTrapBlob::operator new(size_t s, unsigned size) {
- void* p = CodeCache::allocate(size);
- if (!p) fatal("Initial size of CodeCache is too small");
- return p;
-}
#endif // COMPILER2
@@ -498,33 +509,12 @@
blob = new (size) ExceptionBlob(cb, size, oop_maps, frame_size);
}
- // We do not need to hold the CodeCache lock during name formatting
- if (blob != NULL) {
- char blob_id[256];
- jio_snprintf(blob_id, sizeof(blob_id), "ExceptionBlob@" PTR_FORMAT, blob->code_begin());
- if (PrintStubCode) {
- tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob);
- Disassembler::decode(blob->code_begin(), blob->code_end());
- }
- Forte::register_stub(blob_id, blob->code_begin(), blob->code_end());
-
- if (JvmtiExport::should_post_dynamic_code_generated()) {
- JvmtiExport::post_dynamic_code_generated("ExceptionBlob", blob->code_begin(), blob->code_end());
- }
- }
-
- // Track memory usage statistic after releasing CodeCache_lock
- MemoryService::track_code_cache_memory_usage();
+ trace_new_stub(blob, "ExceptionBlob");
return blob;
}
-void* ExceptionBlob::operator new(size_t s, unsigned size) {
- void* p = CodeCache::allocate(size);
- if (!p) fatal("Initial size of CodeCache is too small");
- return p;
-}
#endif // COMPILER2
@@ -554,35 +544,12 @@
blob = new (size) SafepointBlob(cb, size, oop_maps, frame_size);
}
- // We do not need to hold the CodeCache lock during name formatting.
- if (blob != NULL) {
- char blob_id[256];
- jio_snprintf(blob_id, sizeof(blob_id), "SafepointBlob@" PTR_FORMAT, blob->code_begin());
- if (PrintStubCode) {
- tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob);
- Disassembler::decode(blob->code_begin(), blob->code_end());
- }
- Forte::register_stub(blob_id, blob->code_begin(), blob->code_end());
-
- if (JvmtiExport::should_post_dynamic_code_generated()) {
- JvmtiExport::post_dynamic_code_generated("SafepointBlob", blob->code_begin(), blob->code_end());
- }
- }
-
- // Track memory usage statistic after releasing CodeCache_lock
- MemoryService::track_code_cache_memory_usage();
+ trace_new_stub(blob, "SafepointBlob");
return blob;
}
-void* SafepointBlob::operator new(size_t s, unsigned size) {
- void* p = CodeCache::allocate(size);
- if (!p) fatal("Initial size of CodeCache is too small");
- return p;
-}
-
-
//----------------------------------------------------------------------------------------------------
// Verification and printing
--- a/hotspot/src/share/vm/code/codeBlob.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/code/codeBlob.hpp Tue May 24 14:32:10 2011 -0700
@@ -35,6 +35,7 @@
// Suptypes are:
// nmethod : Compiled Java methods (include method that calls to native code)
// RuntimeStub : Call to VM runtime methods
+// RicochetBlob : Used for blocking MethodHandle adapters
// DeoptimizationBlob : Used for deoptimizatation
// ExceptionBlob : Used for stack unrolling
// SafepointBlob : Used to handle illegal instruction exceptions
@@ -95,12 +96,13 @@
void flush();
// Typing
- virtual bool is_buffer_blob() const { return false; }
- virtual bool is_nmethod() const { return false; }
- virtual bool is_runtime_stub() const { return false; }
- virtual bool is_deoptimization_stub() const { return false; }
- virtual bool is_uncommon_trap_stub() const { return false; }
- virtual bool is_exception_stub() const { return false; }
+ virtual bool is_buffer_blob() const { return false; }
+ virtual bool is_nmethod() const { return false; }
+ virtual bool is_runtime_stub() const { return false; }
+ virtual bool is_ricochet_stub() const { return false; }
+ virtual bool is_deoptimization_stub() const { return false; }
+ virtual bool is_uncommon_trap_stub() const { return false; }
+ virtual bool is_exception_stub() const { return false; }
virtual bool is_safepoint_stub() const { return false; }
virtual bool is_adapter_blob() const { return false; }
virtual bool is_method_handles_adapter_blob() const { return false; }
@@ -182,6 +184,9 @@
virtual void print_on(outputStream* st) const;
virtual void print_value_on(outputStream* st) const;
+ // Deal with Disassembler, VTune, Forte, JvmtiExport, MemoryService.
+ static void trace_new_stub(CodeBlob* blob, const char* name1, const char* name2 = "");
+
// Print the comment associated with offset on stream, if there is one
virtual void print_block_comment(outputStream* stream, address block_begin) {
intptr_t offset = (intptr_t)(block_begin - code_begin());
@@ -318,7 +323,11 @@
class SingletonBlob: public CodeBlob {
friend class VMStructs;
- public:
+
+ protected:
+ void* operator new(size_t s, unsigned size);
+
+ public:
SingletonBlob(
const char* name,
CodeBuffer* cb,
@@ -341,6 +350,50 @@
//----------------------------------------------------------------------------------------------------
+// RicochetBlob
+// Holds an arbitrary argument list indefinitely while Java code executes recursively.
+
+class RicochetBlob: public SingletonBlob {
+ friend class VMStructs;
+ private:
+
+ int _bounce_offset;
+ int _exception_offset;
+
+ // Creation support
+ RicochetBlob(
+ CodeBuffer* cb,
+ int size,
+ int bounce_offset,
+ int exception_offset,
+ int frame_size
+ );
+
+ public:
+ // Creation
+ static RicochetBlob* create(
+ CodeBuffer* cb,
+ int bounce_offset,
+ int exception_offset,
+ int frame_size
+ );
+
+ // Typing
+ bool is_ricochet_stub() const { return true; }
+
+ // GC for args
+ void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) { /* Nothing to do */ }
+
+ address bounce_addr() const { return code_begin() + _bounce_offset; }
+ address exception_addr() const { return code_begin() + _exception_offset; }
+ bool returns_to_bounce_addr(address pc) const {
+ address bounce_pc = bounce_addr();
+ return (pc == bounce_pc || (pc + frame::pc_return_offset) == bounce_pc);
+ }
+};
+
+
+//----------------------------------------------------------------------------------------------------
// DeoptimizationBlob
class DeoptimizationBlob: public SingletonBlob {
@@ -363,8 +416,6 @@
int frame_size
);
- void* operator new(size_t s, unsigned size);
-
public:
// Creation
static DeoptimizationBlob* create(
@@ -378,7 +429,6 @@
// Typing
bool is_deoptimization_stub() const { return true; }
- const DeoptimizationBlob *as_deoptimization_stub() const { return this; }
bool exception_address_is_unpack_entry(address pc) const {
address unpack_pc = unpack();
return (pc == unpack_pc || (pc + frame::pc_return_offset) == unpack_pc);
@@ -426,8 +476,6 @@
int frame_size
);
- void* operator new(size_t s, unsigned size);
-
public:
// Creation
static UncommonTrapBlob* create(
@@ -458,8 +506,6 @@
int frame_size
);
- void* operator new(size_t s, unsigned size);
-
public:
// Creation
static ExceptionBlob* create(
@@ -491,8 +537,6 @@
int frame_size
);
- void* operator new(size_t s, unsigned size);
-
public:
// Creation
static SafepointBlob* create(
--- a/hotspot/src/share/vm/code/codeCache.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/code/codeCache.cpp Tue May 24 14:32:10 2011 -0700
@@ -796,6 +796,7 @@
int nmethodCount = 0;
int runtimeStubCount = 0;
int adapterCount = 0;
+ int ricochetStubCount = 0;
int deoptimizationStubCount = 0;
int uncommonTrapStubCount = 0;
int bufferBlobCount = 0;
@@ -840,6 +841,8 @@
}
} else if (cb->is_runtime_stub()) {
runtimeStubCount++;
+ } else if (cb->is_ricochet_stub()) {
+ ricochetStubCount++;
} else if (cb->is_deoptimization_stub()) {
deoptimizationStubCount++;
} else if (cb->is_uncommon_trap_stub()) {
@@ -876,6 +879,7 @@
tty->print_cr("runtime_stubs: %d",runtimeStubCount);
tty->print_cr("adapters: %d",adapterCount);
tty->print_cr("buffer blobs: %d",bufferBlobCount);
+ tty->print_cr("ricochet_stubs: %d",ricochetStubCount);
tty->print_cr("deoptimization_stubs: %d",deoptimizationStubCount);
tty->print_cr("uncommon_traps: %d",uncommonTrapStubCount);
tty->print_cr("\nnmethod size distribution (non-zombie java)");
--- a/hotspot/src/share/vm/compiler/disassembler.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/compiler/disassembler.cpp Tue May 24 14:32:10 2011 -0700
@@ -283,10 +283,10 @@
st->print("Stub::%s", desc->name());
if (desc->begin() != adr)
st->print("%+d 0x%p",adr - desc->begin(), adr);
- else if (WizardMode) st->print(" " INTPTR_FORMAT, adr);
+ else if (WizardMode) st->print(" " PTR_FORMAT, adr);
return;
}
- st->print("Stub::<unknown> " INTPTR_FORMAT, adr);
+ st->print("Stub::<unknown> " PTR_FORMAT, adr);
return;
}
@@ -314,8 +314,8 @@
}
}
- // Fall through to a simple numeral.
- st->print(INTPTR_FORMAT, (intptr_t)adr);
+ // Fall through to a simple (hexadecimal) numeral.
+ st->print(PTR_FORMAT, adr);
}
void decode_env::print_insn_labels() {
@@ -326,7 +326,7 @@
cb->print_block_comment(st, p);
}
if (_print_pc) {
- st->print(" " INTPTR_FORMAT ": ", (intptr_t) p);
+ st->print(" " PTR_FORMAT ": ", p);
}
}
@@ -432,7 +432,7 @@
void Disassembler::decode(CodeBlob* cb, outputStream* st) {
if (!load_library()) return;
decode_env env(cb, st);
- env.output()->print_cr("Decoding CodeBlob " INTPTR_FORMAT, cb);
+ env.output()->print_cr("Decoding CodeBlob " PTR_FORMAT, cb);
env.decode_instructions(cb->code_begin(), cb->code_end());
}
@@ -446,7 +446,7 @@
void Disassembler::decode(nmethod* nm, outputStream* st) {
if (!load_library()) return;
decode_env env(nm, st);
- env.output()->print_cr("Decoding compiled method " INTPTR_FORMAT ":", nm);
+ env.output()->print_cr("Decoding compiled method " PTR_FORMAT ":", nm);
env.output()->print_cr("Code:");
#ifdef SHARK
@@ -478,9 +478,9 @@
int offset = 0;
for (address p = nm->consts_begin(); p < nm->consts_end(); p += 4, offset += 4) {
if ((offset % 8) == 0) {
- env.output()->print_cr(" " INTPTR_FORMAT " (offset: %4d): " PTR32_FORMAT " " PTR64_FORMAT, (intptr_t) p, offset, *((int32_t*) p), *((int64_t*) p));
+ env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT " " PTR64_FORMAT, p, offset, *((int32_t*) p), *((int64_t*) p));
} else {
- env.output()->print_cr(" " INTPTR_FORMAT " (offset: %4d): " PTR32_FORMAT, (intptr_t) p, offset, *((int32_t*) p));
+ env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT, p, offset, *((int32_t*) p));
}
}
}
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Tue May 24 14:32:10 2011 -0700
@@ -2026,7 +2026,7 @@
}
{
- TraceCMSMemoryManagerStats();
+ TraceCMSMemoryManagerStats tmms(gch->gc_cause());
}
GenMarkSweep::invoke_at_safepoint(_cmsGen->level(),
ref_processor(), clear_all_soft_refs);
@@ -3479,7 +3479,7 @@
void CMSCollector::checkpointRootsInitial(bool asynch) {
assert(_collectorState == InitialMarking, "Wrong collector state");
check_correct_thread_executing();
- TraceCMSMemoryManagerStats tms(_collectorState);
+ TraceCMSMemoryManagerStats tms(_collectorState,GenCollectedHeap::heap()->gc_cause());
ReferenceProcessor* rp = ref_processor();
SpecializationStats::clear();
@@ -4858,7 +4858,8 @@
// world is stopped at this checkpoint
assert(SafepointSynchronize::is_at_safepoint(),
"world should be stopped");
- TraceCMSMemoryManagerStats tms(_collectorState);
+ TraceCMSMemoryManagerStats tms(_collectorState,GenCollectedHeap::heap()->gc_cause());
+
verify_work_stacks_empty();
verify_overflow_empty();
@@ -5993,7 +5994,7 @@
verify_work_stacks_empty();
verify_overflow_empty();
increment_sweep_count();
- TraceCMSMemoryManagerStats tms(_collectorState);
+ TraceCMSMemoryManagerStats tms(_collectorState,GenCollectedHeap::heap()->gc_cause());
_inter_sweep_timer.stop();
_inter_sweep_estimate.sample(_inter_sweep_timer.seconds());
@@ -9235,11 +9236,12 @@
return res;
}
-TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase): TraceMemoryManagerStats() {
+TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase, GCCause::Cause cause): TraceMemoryManagerStats() {
switch (phase) {
case CMSCollector::InitialMarking:
initialize(true /* fullGC */ ,
+ cause /* cause of the GC */,
true /* recordGCBeginTime */,
true /* recordPreGCUsage */,
false /* recordPeakUsage */,
@@ -9251,6 +9253,7 @@
case CMSCollector::FinalMarking:
initialize(true /* fullGC */ ,
+ cause /* cause of the GC */,
false /* recordGCBeginTime */,
false /* recordPreGCUsage */,
false /* recordPeakUsage */,
@@ -9262,6 +9265,7 @@
case CMSCollector::Sweeping:
initialize(true /* fullGC */ ,
+ cause /* cause of the GC */,
false /* recordGCBeginTime */,
false /* recordPreGCUsage */,
true /* recordPeakUsage */,
@@ -9277,8 +9281,9 @@
}
// when bailing out of cms in concurrent mode failure
-TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(): TraceMemoryManagerStats() {
+TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(GCCause::Cause cause): TraceMemoryManagerStats() {
initialize(true /* fullGC */ ,
+ cause /* cause of the GC */,
true /* recordGCBeginTime */,
true /* recordPreGCUsage */,
true /* recordPeakUsage */,
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Tue May 24 14:32:10 2011 -0700
@@ -1895,8 +1895,8 @@
class TraceCMSMemoryManagerStats : public TraceMemoryManagerStats {
public:
- TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase);
- TraceCMSMemoryManagerStats();
+ TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase, GCCause::Cause cause);
+ TraceCMSMemoryManagerStats(GCCause::Cause cause);
};
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Tue May 24 14:32:10 2011 -0700
@@ -1162,7 +1162,7 @@
PrintGC, true, gclog_or_tty);
TraceCollectorStats tcs(g1mm()->full_collection_counters());
- TraceMemoryManagerStats tms(true /* fullGC */);
+ TraceMemoryManagerStats tms(true /* fullGC */, gc_cause());
double start = os::elapsedTime();
g1_policy()->record_full_collection_start();
@@ -3202,7 +3202,7 @@
TraceTime t(verbose_str, PrintGC && !PrintGCDetails, true, gclog_or_tty);
TraceCollectorStats tcs(g1mm()->incremental_collection_counters());
- TraceMemoryManagerStats tms(false /* fullGC */);
+ TraceMemoryManagerStats tms(false /* fullGC */, gc_cause());
// If the secondary_free_list is not empty, append it to the
// free_list. No need to wait for the cleanup operation to finish;
--- a/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parNew/parCardTableModRefBS.cpp Tue May 24 14:32:10 2011 -0700
@@ -29,13 +29,14 @@
#include "memory/sharedHeap.hpp"
#include "memory/space.inline.hpp"
#include "memory/universe.hpp"
+#include "oops/oop.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/virtualspace.hpp"
void CardTableModRefBS::non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
- DirtyCardToOopClosure* dcto_cl,
- ClearNoncleanCardWrapper* cl,
+ OopsInGenClosure* cl,
+ CardTableRS* ct,
int n_threads) {
assert(n_threads > 0, "Error: expected n_threads > 0");
assert((n_threads == 1 && ParallelGCThreads == 0) ||
@@ -49,14 +50,14 @@
lowest_non_clean_base_chunk_index,
lowest_non_clean_chunk_size);
- int n_strides = n_threads * StridesPerThread;
+ int n_strides = n_threads * ParGCStridesPerThread;
SequentialSubTasksDone* pst = sp->par_seq_tasks();
pst->set_n_threads(n_threads);
pst->set_n_tasks(n_strides);
int stride = 0;
while (!pst->is_task_claimed(/* reference */ stride)) {
- process_stride(sp, mr, stride, n_strides, dcto_cl, cl,
+ process_stride(sp, mr, stride, n_strides, cl, ct,
lowest_non_clean,
lowest_non_clean_base_chunk_index,
lowest_non_clean_chunk_size);
@@ -79,13 +80,13 @@
process_stride(Space* sp,
MemRegion used,
jint stride, int n_strides,
- DirtyCardToOopClosure* dcto_cl,
- ClearNoncleanCardWrapper* cl,
+ OopsInGenClosure* cl,
+ CardTableRS* ct,
jbyte** lowest_non_clean,
uintptr_t lowest_non_clean_base_chunk_index,
size_t lowest_non_clean_chunk_size) {
- // We don't have to go downwards here; it wouldn't help anyway,
- // because of parallelism.
+ // We go from higher to lower addresses here; it wouldn't help that much
+ // because of the strided parallelism pattern used here.
// Find the first card address of the first chunk in the stride that is
// at least "bottom" of the used region.
@@ -98,25 +99,35 @@
if ((uintptr_t)stride >= start_chunk_stride_num) {
chunk_card_start = (jbyte*)(start_card +
(stride - start_chunk_stride_num) *
- CardsPerStrideChunk);
+ ParGCCardsPerStrideChunk);
} else {
// Go ahead to the next chunk group boundary, then to the requested stride.
chunk_card_start = (jbyte*)(start_card +
(n_strides - start_chunk_stride_num + stride) *
- CardsPerStrideChunk);
+ ParGCCardsPerStrideChunk);
}
while (chunk_card_start < end_card) {
- // We don't have to go downwards here; it wouldn't help anyway,
- // because of parallelism. (We take care with "min_done"; see below.)
+ // Even though we go from lower to higher addresses below, the
+ // strided parallelism can interleave the actual processing of the
+ // dirty pages in various ways. For a specific chunk within this
+ // stride, we take care to avoid double scanning or missing a card
+ // by suitably initializing the "min_done" field in process_chunk_boundaries()
+ // below, together with the dirty region extension accomplished in
+ // DirtyCardToOopClosure::do_MemRegion().
+ jbyte* chunk_card_end = chunk_card_start + ParGCCardsPerStrideChunk;
// Invariant: chunk_mr should be fully contained within the "used" region.
- jbyte* chunk_card_end = chunk_card_start + CardsPerStrideChunk;
MemRegion chunk_mr = MemRegion(addr_for(chunk_card_start),
chunk_card_end >= end_card ?
used.end() : addr_for(chunk_card_end));
assert(chunk_mr.word_size() > 0, "[chunk_card_start > used_end)");
assert(used.contains(chunk_mr), "chunk_mr should be subset of used");
+ DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, precision(),
+ cl->gen_boundary());
+ ClearNoncleanCardWrapper clear_cl(dcto_cl, ct);
+
+
// Process the chunk.
process_chunk_boundaries(sp,
dcto_cl,
@@ -126,17 +137,30 @@
lowest_non_clean_base_chunk_index,
lowest_non_clean_chunk_size);
+ // We want the LNC array updates above in process_chunk_boundaries
+ // to be visible before any of the card table value changes as a
+ // result of the dirty card iteration below.
+ OrderAccess::storestore();
+
// We do not call the non_clean_card_iterate_serial() version because
- // we want to clear the cards, and the ClearNoncleanCardWrapper closure
- // itself does the work of finding contiguous dirty ranges of cards to
- // process (and clear).
- cl->do_MemRegion(chunk_mr);
+ // we want to clear the cards: clear_cl here does the work of finding
+ // contiguous dirty ranges of cards to process and clear.
+ clear_cl.do_MemRegion(chunk_mr);
// Find the next chunk of the stride.
- chunk_card_start += CardsPerStrideChunk * n_strides;
+ chunk_card_start += ParGCCardsPerStrideChunk * n_strides;
}
}
+
+// If you want a talkative process_chunk_boundaries,
+// then #define NOISY(x) x
+#ifdef NOISY
+#error "Encountered a global preprocessor flag, NOISY, which might clash with local definition to follow"
+#else
+#define NOISY(x)
+#endif
+
void
CardTableModRefBS::
process_chunk_boundaries(Space* sp,
@@ -147,126 +171,232 @@
uintptr_t lowest_non_clean_base_chunk_index,
size_t lowest_non_clean_chunk_size)
{
- // We must worry about the chunk boundaries.
+ // We must worry about non-array objects that cross chunk boundaries,
+ // because such objects are both precisely and imprecisely marked:
+ // .. if the head of such an object is dirty, the entire object
+ // needs to be scanned, under the interpretation that this
+ // was an imprecise mark
+ // .. if the head of such an object is not dirty, we can assume
+ // precise marking and it's efficient to scan just the dirty
+ // cards.
+ // In either case, each scanned reference must be scanned precisely
+ // once so as to avoid cloning of a young referent. For efficiency,
+ // our closures depend on this property and do not protect against
+ // double scans.
- // First, set our max_to_do:
- HeapWord* max_to_do = NULL;
uintptr_t cur_chunk_index = addr_to_chunk_index(chunk_mr.start());
cur_chunk_index = cur_chunk_index - lowest_non_clean_base_chunk_index;
+ NOISY(tty->print_cr("===========================================================================");)
+ NOISY(tty->print_cr(" process_chunk_boundary: Called with [" PTR_FORMAT "," PTR_FORMAT ")",
+ chunk_mr.start(), chunk_mr.end());)
+
+ // First, set "our" lowest_non_clean entry, which would be
+ // used by the thread scanning an adjoining left chunk with
+ // a non-array object straddling the mutual boundary.
+ // Find the object that spans our boundary, if one exists.
+ // first_block is the block possibly straddling our left boundary.
+ HeapWord* first_block = sp->block_start(chunk_mr.start());
+ assert((chunk_mr.start() != used.start()) || (first_block == chunk_mr.start()),
+ "First chunk should always have a co-initial block");
+ // Does the block straddle the chunk's left boundary, and is it
+ // a non-array object?
+ if (first_block < chunk_mr.start() // first block straddles left bdry
+ && sp->block_is_obj(first_block) // first block is an object
+ && !(oop(first_block)->is_objArray() // first block is not an array (arrays are precisely dirtied)
+ || oop(first_block)->is_typeArray())) {
+ // Find our least non-clean card, so that a left neighbour
+ // does not scan an object straddling the mutual boundary
+ // too far to the right, and attempt to scan a portion of
+ // that object twice.
+ jbyte* first_dirty_card = NULL;
+ jbyte* last_card_of_first_obj =
+ byte_for(first_block + sp->block_size(first_block) - 1);
+ jbyte* first_card_of_cur_chunk = byte_for(chunk_mr.start());
+ jbyte* last_card_of_cur_chunk = byte_for(chunk_mr.last());
+ jbyte* last_card_to_check =
+ (jbyte*) MIN2((intptr_t) last_card_of_cur_chunk,
+ (intptr_t) last_card_of_first_obj);
+ // Note that this does not need to go beyond our last card
+ // if our first object completely straddles this chunk.
+ for (jbyte* cur = first_card_of_cur_chunk;
+ cur <= last_card_to_check; cur++) {
+ jbyte val = *cur;
+ if (card_will_be_scanned(val)) {
+ first_dirty_card = cur; break;
+ } else {
+ assert(!card_may_have_been_dirty(val), "Error");
+ }
+ }
+ if (first_dirty_card != NULL) {
+ NOISY(tty->print_cr(" LNC: Found a dirty card at " PTR_FORMAT " in current chunk",
+ first_dirty_card);)
+ assert(0 <= cur_chunk_index && cur_chunk_index < lowest_non_clean_chunk_size,
+ "Bounds error.");
+ assert(lowest_non_clean[cur_chunk_index] == NULL,
+ "Write exactly once : value should be stable hereafter for this round");
+ lowest_non_clean[cur_chunk_index] = first_dirty_card;
+ } NOISY(else {
+ tty->print_cr(" LNC: Found no dirty card in current chunk; leaving LNC entry NULL");
+ // In the future, we could have this thread look for a non-NULL value to copy from its
+ // right neighbour (up to the end of the first object).
+ if (last_card_of_cur_chunk < last_card_of_first_obj) {
+ tty->print_cr(" LNC: BEWARE!!! first obj straddles past right end of chunk:\n"
+ " might be efficient to get value from right neighbour?");
+ }
+ })
+ } else {
+ // In this case we can help our neighbour by just asking them
+ // to stop at our first card (even though it may not be dirty).
+ NOISY(tty->print_cr(" LNC: first block is not a non-array object; setting LNC to first card of current chunk");)
+ assert(lowest_non_clean[cur_chunk_index] == NULL, "Write once : value should be stable hereafter");
+ jbyte* first_card_of_cur_chunk = byte_for(chunk_mr.start());
+ lowest_non_clean[cur_chunk_index] = first_card_of_cur_chunk;
+ }
+ NOISY(tty->print_cr(" process_chunk_boundary: lowest_non_clean[" INTPTR_FORMAT "] = " PTR_FORMAT
+ " which corresponds to the heap address " PTR_FORMAT,
+ cur_chunk_index, lowest_non_clean[cur_chunk_index],
+ (lowest_non_clean[cur_chunk_index] != NULL)
+ ? addr_for(lowest_non_clean[cur_chunk_index])
+ : NULL);)
+ NOISY(tty->print_cr("---------------------------------------------------------------------------");)
+
+ // Next, set our own max_to_do, which will strictly/exclusively bound
+ // the highest address that we will scan past the right end of our chunk.
+ HeapWord* max_to_do = NULL;
if (chunk_mr.end() < used.end()) {
- // This is not the last chunk in the used region. What is the last
- // object?
- HeapWord* last_block = sp->block_start(chunk_mr.end());
+ // This is not the last chunk in the used region.
+ // What is our last block? We check the first block of
+ // the next (right) chunk rather than strictly check our last block
+ // because it's potentially more efficient to do so.
+ HeapWord* const last_block = sp->block_start(chunk_mr.end());
assert(last_block <= chunk_mr.end(), "In case this property changes.");
- if (last_block == chunk_mr.end()
- || !sp->block_is_obj(last_block)) {
+ if ((last_block == chunk_mr.end()) // our last block does not straddle boundary
+ || !sp->block_is_obj(last_block) // last_block isn't an object
+ || oop(last_block)->is_objArray() // last_block is an array (precisely marked)
+ || oop(last_block)->is_typeArray()) {
max_to_do = chunk_mr.end();
-
+ NOISY(tty->print_cr(" process_chunk_boundary: Last block on this card is not a non-array object;\n"
+ " max_to_do left at " PTR_FORMAT, max_to_do);)
} else {
- // It is an object and starts before the end of the current chunk.
+ assert(last_block < chunk_mr.end(), "Tautology");
+ // It is a non-array object that straddles the right boundary of this chunk.
// last_obj_card is the card corresponding to the start of the last object
// in the chunk. Note that the last object may not start in
// the chunk.
- jbyte* last_obj_card = byte_for(last_block);
- if (!card_may_have_been_dirty(*last_obj_card)) {
- // The card containing the head is not dirty. Any marks in
+ jbyte* const last_obj_card = byte_for(last_block);
+ const jbyte val = *last_obj_card;
+ if (!card_will_be_scanned(val)) {
+ assert(!card_may_have_been_dirty(val), "Error");
+ // The card containing the head is not dirty. Any marks on
// subsequent cards still in this chunk must have been made
- // precisely; we can cap processing at the end.
+ // precisely; we can cap processing at the end of our chunk.
max_to_do = chunk_mr.end();
+ NOISY(tty->print_cr(" process_chunk_boundary: Head of last object on this card is not dirty;\n"
+ " max_to_do left at " PTR_FORMAT,
+ max_to_do);)
} else {
// The last object must be considered dirty, and extends onto the
// following chunk. Look for a dirty card in that chunk that will
// bound our processing.
jbyte* limit_card = NULL;
- size_t last_block_size = sp->block_size(last_block);
- jbyte* last_card_of_last_obj =
+ const size_t last_block_size = sp->block_size(last_block);
+ jbyte* const last_card_of_last_obj =
byte_for(last_block + last_block_size - 1);
- jbyte* first_card_of_next_chunk = byte_for(chunk_mr.end());
+ jbyte* const first_card_of_next_chunk = byte_for(chunk_mr.end());
// This search potentially goes a long distance looking
- // for the next card that will be scanned. For example,
- // an object that is an array of primitives will not
- // have any cards covering regions interior to the array
- // that will need to be scanned. The scan can be terminated
- // at the last card of the next chunk. That would leave
- // limit_card as NULL and would result in "max_to_do"
- // being set with the LNC value or with the end
- // of the last block.
- jbyte* last_card_of_next_chunk = first_card_of_next_chunk +
- CardsPerStrideChunk;
- assert(byte_for(chunk_mr.end()) - byte_for(chunk_mr.start())
- == CardsPerStrideChunk, "last card of next chunk may be wrong");
- jbyte* last_card_to_check = (jbyte*) MIN2(last_card_of_last_obj,
- last_card_of_next_chunk);
+ // for the next card that will be scanned, terminating
+ // at the end of the last_block, if no earlier dirty card
+ // is found.
+ assert(byte_for(chunk_mr.end()) - byte_for(chunk_mr.start()) == ParGCCardsPerStrideChunk,
+ "last card of next chunk may be wrong");
for (jbyte* cur = first_card_of_next_chunk;
- cur <= last_card_to_check; cur++) {
- if (card_will_be_scanned(*cur)) {
+ cur <= last_card_of_last_obj; cur++) {
+ const jbyte val = *cur;
+ if (card_will_be_scanned(val)) {
+ NOISY(tty->print_cr(" Found a non-clean card " PTR_FORMAT " with value 0x%x",
+ cur, (int)val);)
limit_card = cur; break;
+ } else {
+ assert(!card_may_have_been_dirty(val), "Error: card can't be skipped");
}
}
- assert(0 <= cur_chunk_index+1 &&
- cur_chunk_index+1 < lowest_non_clean_chunk_size,
+ if (limit_card != NULL) {
+ max_to_do = addr_for(limit_card);
+ assert(limit_card != NULL && max_to_do != NULL, "Error");
+ NOISY(tty->print_cr(" process_chunk_boundary: Found a dirty card at " PTR_FORMAT
+ " max_to_do set at " PTR_FORMAT " which is before end of last block in chunk: "
+ PTR_FORMAT " + " PTR_FORMAT " = " PTR_FORMAT,
+ limit_card, max_to_do, last_block, last_block_size, (last_block+last_block_size));)
+ } else {
+ // The following is a pessimistic value, because it's possible
+ // that a dirty card on a subsequent chunk has been cleared by
+ // the time we get to look at it; we'll correct for that further below,
+ // using the LNC array which records the least non-clean card
+ // before cards were cleared in a particular chunk.
+ limit_card = last_card_of_last_obj;
+ max_to_do = last_block + last_block_size;
+ assert(limit_card != NULL && max_to_do != NULL, "Error");
+ NOISY(tty->print_cr(" process_chunk_boundary: Found no dirty card before end of last block in chunk\n"
+ " Setting limit_card to " PTR_FORMAT
+ " and max_to_do " PTR_FORMAT " + " PTR_FORMAT " = " PTR_FORMAT,
+ limit_card, last_block, last_block_size, max_to_do);)
+ }
+ assert(0 < cur_chunk_index+1 && cur_chunk_index+1 < lowest_non_clean_chunk_size,
"Bounds error.");
- // LNC for the next chunk
- jbyte* lnc_card = lowest_non_clean[cur_chunk_index+1];
- if (limit_card == NULL) {
- limit_card = lnc_card;
- }
- if (limit_card != NULL) {
+ // It is possible that a dirty card for the last object may have been
+ // cleared before we had a chance to examine it. In that case, the value
+ // will have been logged in the LNC for that chunk.
+ // We need to examine as many chunks to the right as this object
+ // covers.
+ const uintptr_t last_chunk_index_to_check = addr_to_chunk_index(last_block + last_block_size - 1)
+ - lowest_non_clean_base_chunk_index;
+ DEBUG_ONLY(const uintptr_t last_chunk_index = addr_to_chunk_index(used.last())
+ - lowest_non_clean_base_chunk_index;)
+ assert(last_chunk_index_to_check <= last_chunk_index,
+ err_msg("Out of bounds: last_chunk_index_to_check " INTPTR_FORMAT
+ " exceeds last_chunk_index " INTPTR_FORMAT,
+ last_chunk_index_to_check, last_chunk_index));
+ for (uintptr_t lnc_index = cur_chunk_index + 1;
+ lnc_index <= last_chunk_index_to_check;
+ lnc_index++) {
+ jbyte* lnc_card = lowest_non_clean[lnc_index];
if (lnc_card != NULL) {
- limit_card = (jbyte*)MIN2((intptr_t)limit_card,
- (intptr_t)lnc_card);
- }
- max_to_do = addr_for(limit_card);
- } else {
- max_to_do = last_block + last_block_size;
+ // we can stop at the first non-NULL entry we find
+ if (lnc_card <= limit_card) {
+ NOISY(tty->print_cr(" process_chunk_boundary: LNC card " PTR_FORMAT " is lower than limit_card " PTR_FORMAT,
+ " max_to_do will be lowered to " PTR_FORMAT " from " PTR_FORMAT,
+ lnc_card, limit_card, addr_for(lnc_card), max_to_do);)
+ limit_card = lnc_card;
+ max_to_do = addr_for(limit_card);
+ assert(limit_card != NULL && max_to_do != NULL, "Error");
+ }
+ // In any case, we break now
+ break;
+ } // else continue to look for a non-NULL entry if any
}
+ assert(limit_card != NULL && max_to_do != NULL, "Error");
}
+ assert(max_to_do != NULL, "OOPS 1 !");
}
- assert(max_to_do != NULL, "OOPS!");
+ assert(max_to_do != NULL, "OOPS 2!");
} else {
max_to_do = used.end();
+ NOISY(tty->print_cr(" process_chunk_boundary: Last chunk of this space;\n"
+ " max_to_do left at " PTR_FORMAT,
+ max_to_do);)
}
+ assert(max_to_do != NULL, "OOPS 3!");
// Now we can set the closure we're using so it doesn't to beyond
// max_to_do.
dcto_cl->set_min_done(max_to_do);
#ifndef PRODUCT
dcto_cl->set_last_bottom(max_to_do);
#endif
+ NOISY(tty->print_cr("===========================================================================\n");)
+}
- // Now we set *our" lowest_non_clean entry.
- // Find the object that spans our boundary, if one exists.
- // Nothing to do on the first chunk.
- if (chunk_mr.start() > used.start()) {
- // first_block is the block possibly spanning the chunk start
- HeapWord* first_block = sp->block_start(chunk_mr.start());
- // Does the block span the start of the chunk and is it
- // an object?
- if (first_block < chunk_mr.start() &&
- sp->block_is_obj(first_block)) {
- jbyte* first_dirty_card = NULL;
- jbyte* last_card_of_first_obj =
- byte_for(first_block + sp->block_size(first_block) - 1);
- jbyte* first_card_of_cur_chunk = byte_for(chunk_mr.start());
- jbyte* last_card_of_cur_chunk = byte_for(chunk_mr.last());
- jbyte* last_card_to_check =
- (jbyte*) MIN2((intptr_t) last_card_of_cur_chunk,
- (intptr_t) last_card_of_first_obj);
- for (jbyte* cur = first_card_of_cur_chunk;
- cur <= last_card_to_check; cur++) {
- if (card_will_be_scanned(*cur)) {
- first_dirty_card = cur; break;
- }
- }
- if (first_dirty_card != NULL) {
- assert(0 <= cur_chunk_index &&
- cur_chunk_index < lowest_non_clean_chunk_size,
- "Bounds error.");
- lowest_non_clean[cur_chunk_index] = first_dirty_card;
- }
- }
- }
-}
+#undef NOISY
void
CardTableModRefBS::
@@ -283,8 +413,8 @@
// LNC array for the covered region. Any later expansion can't affect
// the used_at_save_marks region.
// (I observed a bug in which the first thread to execute this would
- // resize, and then it would cause "expand_and_allocates" that would
- // Increase the number of chunks in the covered region. Then a second
+ // resize, and then it would cause "expand_and_allocate" that would
+ // increase the number of chunks in the covered region. Then a second
// thread would come and execute this, see that the size didn't match,
// and free and allocate again. So the first thread would be using a
// freed "_lowest_non_clean" array.)
--- a/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parNew/parOopClosures.inline.hpp Tue May 24 14:32:10 2011 -0700
@@ -77,7 +77,23 @@
if (!oopDesc::is_null(heap_oop)) {
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
if ((HeapWord*)obj < _boundary) {
- assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?");
+#ifndef PRODUCT
+ if (_g->to()->is_in_reserved(obj)) {
+ tty->print_cr("Scanning field (" PTR_FORMAT ") twice?", p);
+ GenCollectedHeap* gch = (GenCollectedHeap*)Universe::heap();
+ Space* sp = gch->space_containing(p);
+ oop obj = oop(sp->block_start(p));
+ assert((HeapWord*)obj < (HeapWord*)p, "Error");
+ tty->print_cr("Object: " PTR_FORMAT, obj);
+ tty->print_cr("-------");
+ obj->print();
+ tty->print_cr("-----");
+ tty->print_cr("Heap:");
+ tty->print_cr("-----");
+ gch->print();
+ ShouldNotReachHere();
+ }
+#endif
// OK, we need to ensure that it is copied.
// We read the klass and mark in this order, so that we can reliably
// get the size of the object: if the mark we read is not a
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Tue May 24 14:32:10 2011 -0700
@@ -173,7 +173,7 @@
TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty);
TraceTime t1(gc_cause_str, PrintGC, !PrintGCDetails, gclog_or_tty);
TraceCollectorStats tcs(counters());
- TraceMemoryManagerStats tms(true /* Full GC */);
+ TraceMemoryManagerStats tms(true /* Full GC */,gc_cause);
if (TraceGen1Time) accumulated_time()->start();
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Tue May 24 14:32:10 2011 -0700
@@ -2057,7 +2057,7 @@
TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty);
TraceTime t1(gc_cause_str, PrintGC, !PrintGCDetails, gclog_or_tty);
TraceCollectorStats tcs(counters());
- TraceMemoryManagerStats tms(true /* Full GC */);
+ TraceMemoryManagerStats tms(true /* Full GC */,gc_cause);
if (TraceGen1Time) accumulated_time()->start();
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp Tue May 24 14:32:10 2011 -0700
@@ -322,7 +322,7 @@
TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty);
TraceTime t1("GC", PrintGC, !PrintGCDetails, gclog_or_tty);
TraceCollectorStats tcs(counters());
- TraceMemoryManagerStats tms(false /* not full GC */);
+ TraceMemoryManagerStats tms(false /* not full GC */,gc_cause);
if (TraceGen0Time) accumulated_time()->start();
--- a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp Tue May 24 14:32:10 2011 -0700
@@ -175,19 +175,32 @@
int temps,
int popframe_args,
int monitors,
+ int caller_actual_parameters,
int callee_params,
int callee_locals,
- bool is_top_frame);
+ bool is_top_frame) {
+ return layout_activation(method,
+ temps,
+ popframe_args,
+ monitors,
+ caller_actual_parameters,
+ callee_params,
+ callee_locals,
+ (frame*)NULL,
+ (frame*)NULL,
+ is_top_frame);
+ }
static int layout_activation(methodOop method,
- int temps,
- int popframe_args,
- int monitors,
- int callee_params,
- int callee_locals,
- frame* caller,
- frame* interpreter_frame,
- bool is_top_frame);
+ int temps,
+ int popframe_args,
+ int monitors,
+ int caller_actual_parameters,
+ int callee_params,
+ int callee_locals,
+ frame* caller,
+ frame* interpreter_frame,
+ bool is_top_frame);
// Runtime support
static bool is_not_reached( methodHandle method, int bci);
--- a/hotspot/src/share/vm/memory/blockOffsetTable.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/memory/blockOffsetTable.cpp Tue May 24 14:32:10 2011 -0700
@@ -541,20 +541,33 @@
// to go back by.
size_t n_cards_back = entry_to_cards_back(offset);
q -= (N_words * n_cards_back);
- assert(q >= _sp->bottom(), "Went below bottom!");
+ assert(q >= _sp->bottom(),
+ err_msg("q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT,
+ q, _sp->bottom()));
+ assert(q < _sp->end(),
+ err_msg("q = " PTR_FORMAT " crossed above end = " PTR_FORMAT,
+ q, _sp->end()));
index -= n_cards_back;
offset = _array->offset_array(index);
}
assert(offset < N_words, "offset too large");
index--;
q -= offset;
+ assert(q >= _sp->bottom(),
+ err_msg("q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT,
+ q, _sp->bottom()));
+ assert(q < _sp->end(),
+ err_msg("q = " PTR_FORMAT " crossed above end = " PTR_FORMAT,
+ q, _sp->end()));
HeapWord* n = q;
while (n <= addr) {
debug_only(HeapWord* last = q); // for debugging
q = n;
n += _sp->block_size(n);
- assert(n > q, err_msg("Looping at: " INTPTR_FORMAT, n));
+ assert(n > q,
+ err_msg("Looping at n = " PTR_FORMAT " with last = " PTR_FORMAT " _sp = [" PTR_FORMAT "," PTR_FORMAT ")",
+ n, last, _sp->bottom(), _sp->end()));
}
assert(q <= addr, err_msg("wrong order for current (" INTPTR_FORMAT ") <= arg (" INTPTR_FORMAT ")", q, addr));
assert(addr <= n, err_msg("wrong order for arg (" INTPTR_FORMAT ") <= next (" INTPTR_FORMAT ")", addr, n));
--- a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp Tue May 24 14:32:10 2011 -0700
@@ -455,25 +455,29 @@
return true;
}
-
void CardTableModRefBS::non_clean_card_iterate_possibly_parallel(Space* sp,
MemRegion mr,
- DirtyCardToOopClosure* dcto_cl,
- ClearNoncleanCardWrapper* cl) {
+ OopsInGenClosure* cl,
+ CardTableRS* ct) {
if (!mr.is_empty()) {
int n_threads = SharedHeap::heap()->n_par_threads();
if (n_threads > 0) {
#ifndef SERIALGC
- non_clean_card_iterate_parallel_work(sp, mr, dcto_cl, cl, n_threads);
+ non_clean_card_iterate_parallel_work(sp, mr, cl, ct, n_threads);
#else // SERIALGC
fatal("Parallel gc not supported here.");
#endif // SERIALGC
} else {
// We do not call the non_clean_card_iterate_serial() version below because
// we want to clear the cards (which non_clean_card_iterate_serial() does not
- // do for us), and the ClearNoncleanCardWrapper closure itself does the work
- // of finding contiguous dirty ranges of cards to process (and clear).
- cl->do_MemRegion(mr);
+ // do for us): clear_cl here does the work of finding contiguous dirty ranges
+ // of cards to process and clear.
+
+ DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, precision(),
+ cl->gen_boundary());
+ ClearNoncleanCardWrapper clear_cl(dcto_cl, ct);
+
+ clear_cl.do_MemRegion(mr);
}
}
}
--- a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp Tue May 24 14:32:10 2011 -0700
@@ -150,7 +150,9 @@
// Mapping from address to card marking array entry
jbyte* byte_for(const void* p) const {
assert(_whole_heap.contains(p),
- "out of bounds access to card marking array");
+ err_msg("Attempt to access p = "PTR_FORMAT" out of bounds of "
+ " card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")",
+ p, _whole_heap.start(), _whole_heap.end()));
jbyte* result = &byte_map_base[uintptr_t(p) >> card_shift];
assert(result >= _byte_map && result < _byte_map + _byte_map_size,
"out of bounds accessor for card marking array");
@@ -173,18 +175,17 @@
// A variant of the above that will operate in a parallel mode if
// worker threads are available, and clear the dirty cards as it
// processes them.
- // ClearNoncleanCardWrapper cl must wrap the DirtyCardToOopClosure dcto_cl,
- // which may itself be modified by the method.
+ // XXX ??? MemRegionClosure above vs OopsInGenClosure below XXX
+ // XXX some new_dcto_cl's take OopClosure's, plus as above there are
+ // some MemRegionClosures. Clean this up everywhere. XXX
void non_clean_card_iterate_possibly_parallel(Space* sp, MemRegion mr,
- DirtyCardToOopClosure* dcto_cl,
- ClearNoncleanCardWrapper* cl);
+ OopsInGenClosure* cl, CardTableRS* ct);
private:
// Work method used to implement non_clean_card_iterate_possibly_parallel()
// above in the parallel case.
void non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
- DirtyCardToOopClosure* dcto_cl,
- ClearNoncleanCardWrapper* cl,
+ OopsInGenClosure* cl, CardTableRS* ct,
int n_threads);
protected:
@@ -198,11 +199,6 @@
// *** Support for parallel card scanning.
- enum SomeConstantsForParallelism {
- StridesPerThread = 2,
- CardsPerStrideChunk = 256
- };
-
// This is an array, one element per covered region of the card table.
// Each entry is itself an array, with one element per chunk in the
// covered region. Each entry of these arrays is the lowest non-clean
@@ -235,7 +231,7 @@
// covers the given address.
uintptr_t addr_to_chunk_index(const void* addr) {
uintptr_t card = (uintptr_t) byte_for(addr);
- return card / CardsPerStrideChunk;
+ return card / ParGCCardsPerStrideChunk;
}
// Apply cl, which must either itself apply dcto_cl or be dcto_cl,
@@ -243,8 +239,8 @@
void process_stride(Space* sp,
MemRegion used,
jint stride, int n_strides,
- DirtyCardToOopClosure* dcto_cl,
- ClearNoncleanCardWrapper* cl,
+ OopsInGenClosure* cl,
+ CardTableRS* ct,
jbyte** lowest_non_clean,
uintptr_t lowest_non_clean_base_chunk_index,
size_t lowest_non_clean_chunk_size);
@@ -457,14 +453,18 @@
size_t delta = pointer_delta(p, byte_map_base, sizeof(jbyte));
HeapWord* result = (HeapWord*) (delta << card_shift);
assert(_whole_heap.contains(result),
- "out of bounds accessor from card marking array");
+ err_msg("Returning result = "PTR_FORMAT" out of bounds of "
+ " card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")",
+ result, _whole_heap.start(), _whole_heap.end()));
return result;
}
// Mapping from address to card marking array index.
size_t index_for(void* p) {
assert(_whole_heap.contains(p),
- "out of bounds access to card marking array");
+ err_msg("Attempt to access p = "PTR_FORMAT" out of bounds of "
+ " card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")",
+ p, _whole_heap.start(), _whole_heap.end()));
return byte_for(p) - _byte_map;
}
@@ -482,7 +482,7 @@
void verify_dirty_region(MemRegion mr) PRODUCT_RETURN;
static size_t par_chunk_heapword_alignment() {
- return CardsPerStrideChunk * card_size_in_words;
+ return ParGCCardsPerStrideChunk * card_size_in_words;
}
};
--- a/hotspot/src/share/vm/memory/cardTableRS.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/memory/cardTableRS.cpp Tue May 24 14:32:10 2011 -0700
@@ -162,7 +162,7 @@
}
ClearNoncleanCardWrapper::ClearNoncleanCardWrapper(
- MemRegionClosure* dirty_card_closure, CardTableRS* ct) :
+ DirtyCardToOopClosure* dirty_card_closure, CardTableRS* ct) :
_dirty_card_closure(dirty_card_closure), _ct(ct) {
_is_par = (SharedHeap::heap()->n_par_threads() > 0);
}
@@ -246,10 +246,6 @@
void CardTableRS::younger_refs_in_space_iterate(Space* sp,
OopsInGenClosure* cl) {
- DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, _ct_bs->precision(),
- cl->gen_boundary());
- ClearNoncleanCardWrapper clear_cl(dcto_cl, this);
-
const MemRegion urasm = sp->used_region_at_save_marks();
#ifdef ASSERT
// Convert the assertion check to a warning if we are running
@@ -275,10 +271,10 @@
if (!urasm.equals(urasm2)) {
warning("CMS+ParNew: Flickering used_region_at_save_marks()!!");
}
+ ShouldNotReachHere();
}
#endif
- _ct_bs->non_clean_card_iterate_possibly_parallel(sp, urasm,
- dcto_cl, &clear_cl);
+ _ct_bs->non_clean_card_iterate_possibly_parallel(sp, urasm, cl, this);
}
void CardTableRS::clear_into_younger(Generation* gen, bool clear_perm) {
--- a/hotspot/src/share/vm/memory/cardTableRS.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/memory/cardTableRS.hpp Tue May 24 14:32:10 2011 -0700
@@ -31,7 +31,6 @@
class Space;
class OopsInGenClosure;
-class DirtyCardToOopClosure;
// This kind of "GenRemSet" uses a card table both as shared data structure
// for a mod ref barrier set and for the rem set information.
@@ -167,7 +166,7 @@
};
class ClearNoncleanCardWrapper: public MemRegionClosure {
- MemRegionClosure* _dirty_card_closure;
+ DirtyCardToOopClosure* _dirty_card_closure;
CardTableRS* _ct;
bool _is_par;
private:
@@ -179,7 +178,7 @@
inline bool clear_card_parallel(jbyte* entry);
public:
- ClearNoncleanCardWrapper(MemRegionClosure* dirty_card_closure, CardTableRS* ct);
+ ClearNoncleanCardWrapper(DirtyCardToOopClosure* dirty_card_closure, CardTableRS* ct);
void do_MemRegion(MemRegion mr);
};
--- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp Tue May 24 14:32:10 2011 -0700
@@ -537,7 +537,7 @@
// Timer for individual generations. Last argument is false: no CR
TraceTime t1(_gens[i]->short_name(), PrintGCDetails, false, gclog_or_tty);
TraceCollectorStats tcs(_gens[i]->counters());
- TraceMemoryManagerStats tmms(_gens[i]->kind());
+ TraceMemoryManagerStats tmms(_gens[i]->kind(),gc_cause());
size_t prev_used = _gens[i]->used();
_gens[i]->stat_record()->invocations++;
--- a/hotspot/src/share/vm/memory/space.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/memory/space.cpp Tue May 24 14:32:10 2011 -0700
@@ -97,6 +97,14 @@
}
}
+// We get called with "mr" representing the dirty region
+// that we want to process. Because of imprecise marking,
+// we may need to extend the incoming "mr" to the right,
+// and scan more. However, because we may already have
+// scanned some of that extended region, we may need to
+// trim its right-end back some so we do not scan what
+// we (or another worker thread) may already have scanned
+// or planning to scan.
void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) {
// Some collectors need to do special things whenever their dirty
@@ -148,7 +156,7 @@
// e.g. the dirty card region is entirely in a now free object
// -- something that could happen with a concurrent sweeper.
bottom = MIN2(bottom, top);
- mr = MemRegion(bottom, top);
+ MemRegion extended_mr = MemRegion(bottom, top);
assert(bottom <= top &&
(_precision != CardTableModRefBS::ObjHeadPreciseArray ||
_min_done == NULL ||
@@ -156,8 +164,8 @@
"overlap!");
// Walk the region if it is not empty; otherwise there is nothing to do.
- if (!mr.is_empty()) {
- walk_mem_region(mr, bottom_obj, top);
+ if (!extended_mr.is_empty()) {
+ walk_mem_region(extended_mr, bottom_obj, top);
}
// An idempotent closure might be applied in any order, so we don't
--- a/hotspot/src/share/vm/oops/constantPoolKlass.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/oops/constantPoolKlass.cpp Tue May 24 14:32:10 2011 -0700
@@ -285,10 +285,9 @@
void constantPoolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_constantPool(), "should be constant pool");
constantPoolOop cp = (constantPoolOop) obj;
- if (cp->tags() != NULL &&
- (!JavaObjectsInPerm || (EnableInvokeDynamic && cp->has_pseudo_string()))) {
+ if (cp->tags() != NULL) {
for (int i = 1; i < cp->length(); ++i) {
- if (cp->tag_at(i).is_string()) {
+ if (cp->is_pointer_entry(i)) {
oop* base = cp->obj_at_addr_raw(i);
if (PSScavenge::should_scavenge(base)) {
pm->claim_or_forward_depth(base);
@@ -342,6 +341,11 @@
anObj->print_value_on(st);
st->print(" {0x%lx}", (address)anObj);
break;
+ case JVM_CONSTANT_Object :
+ anObj = cp->object_at(index);
+ anObj->print_value_on(st);
+ st->print(" {0x%lx}", (address)anObj);
+ break;
case JVM_CONSTANT_Integer :
st->print("%d", cp->int_at(index));
break;
@@ -432,23 +436,21 @@
guarantee(cp->is_perm(), "should be in permspace");
if (!cp->partially_loaded()) {
for (int i = 0; i< cp->length(); i++) {
+ constantTag tag = cp->tag_at(i);
CPSlot entry = cp->slot_at(i);
- if (cp->tag_at(i).is_klass()) {
+ if (tag.is_klass()) {
if (entry.is_oop()) {
guarantee(entry.get_oop()->is_perm(), "should be in permspace");
guarantee(entry.get_oop()->is_klass(), "should be klass");
}
- }
- if (cp->tag_at(i).is_unresolved_klass()) {
+ } else if (tag.is_unresolved_klass()) {
if (entry.is_oop()) {
guarantee(entry.get_oop()->is_perm(), "should be in permspace");
guarantee(entry.get_oop()->is_klass(), "should be klass");
}
- }
- if (cp->tag_at(i).is_symbol()) {
+ } else if (tag.is_symbol()) {
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
- }
- if (cp->tag_at(i).is_unresolved_string()) {
+ } else if (tag.is_unresolved_string()) {
if (entry.is_oop()) {
guarantee(entry.get_oop()->is_perm(), "should be in permspace");
guarantee(entry.get_oop()->is_instance(), "should be instance");
@@ -456,8 +458,7 @@
else {
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
}
- }
- if (cp->tag_at(i).is_string()) {
+ } else if (tag.is_string()) {
if (!cp->has_pseudo_string()) {
if (entry.is_oop()) {
guarantee(!JavaObjectsInPerm || entry.get_oop()->is_perm(),
@@ -467,8 +468,11 @@
} else {
// can be non-perm, can be non-instance (array)
}
+ } else if (tag.is_object()) {
+ assert(entry.get_oop()->is_oop(), "should be some valid oop");
+ } else {
+ assert(!cp->is_pointer_entry(i), "unhandled oop type in constantPoolKlass::verify_on");
}
- // FIXME: verify JSR 292 tags JVM_CONSTANT_MethodHandle, etc.
}
guarantee(cp->tags()->is_perm(), "should be in permspace");
guarantee(cp->tags()->is_typeArray(), "should be type array");
--- a/hotspot/src/share/vm/opto/bytecodeInfo.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/opto/bytecodeInfo.cpp Tue May 24 14:32:10 2011 -0700
@@ -89,7 +89,7 @@
}
// positive filter: should send be inlined? returns NULL, if yes, or rejection msg
-const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const {
+const char* InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const {
// Allows targeted inlining
if(callee_method->should_inline()) {
*wci_result = *(WarmCallInfo::always_hot());
@@ -102,8 +102,7 @@
// positive filter: should send be inlined? returns NULL (--> yes)
// or rejection msg
- int max_size = C->max_inline_size();
- int size = callee_method->code_size();
+ int size = callee_method->code_size();
// Check for too many throws (and not too huge)
if(callee_method->interpreter_throwout_count() > InlineThrowCount &&
@@ -120,18 +119,36 @@
return NULL; // size and frequency are represented in a new way
}
+ int default_max_inline_size = C->max_inline_size();
+ int inline_small_code_size = InlineSmallCode / 4;
+ int max_inline_size = default_max_inline_size;
+
int call_site_count = method()->scale_count(profile.count());
int invoke_count = method()->interpreter_invocation_count();
- assert( invoke_count != 0, "Require invokation count greater than zero");
- int freq = call_site_count/invoke_count;
+
+ // Bytecoded method handle adapters do not have interpreter
+ // profiling data but only made up MDO data. Get the counter from
+ // there.
+ if (caller_method->is_method_handle_adapter()) {
+ assert(method()->method_data_or_null(), "must have an MDO");
+ ciMethodData* mdo = method()->method_data();
+ ciProfileData* mha_profile = mdo->bci_to_data(caller_bci);
+ assert(mha_profile, "must exist");
+ CounterData* cd = mha_profile->as_CounterData();
+ invoke_count = cd->count();
+ call_site_count = invoke_count; // use the same value
+ }
+
+ assert(invoke_count != 0, "require invocation count greater than zero");
+ int freq = call_site_count / invoke_count;
// bump the max size if the call is frequent
if ((freq >= InlineFrequencyRatio) ||
(call_site_count >= InlineFrequencyCount) ||
is_init_with_ea(callee_method, caller_method, C)) {
- max_size = C->freq_inline_size();
- if (size <= max_size && TraceFrequencyInlining) {
+ max_inline_size = C->freq_inline_size();
+ if (size <= max_inline_size && TraceFrequencyInlining) {
CompileTask::print_inline_indent(inline_depth());
tty->print_cr("Inlined frequent method (freq=%d count=%d):", freq, call_site_count);
CompileTask::print_inline_indent(inline_depth());
@@ -141,11 +158,11 @@
} else {
// Not hot. Check for medium-sized pre-existing nmethod at cold sites.
if (callee_method->has_compiled_code() &&
- callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode/4)
+ callee_method->instructions_size(CompLevel_full_optimization) > inline_small_code_size)
return "already compiled into a medium method";
}
- if (size > max_size) {
- if (max_size > C->max_inline_size())
+ if (size > max_inline_size) {
+ if (max_inline_size > default_max_inline_size)
return "hot method too big";
return "too big";
}
@@ -154,7 +171,7 @@
// negative filter: should send NOT be inlined? returns NULL, ok to inline, or rejection msg
-const char* InlineTree::shouldNotInline(ciMethod *callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const {
+const char* InlineTree::should_not_inline(ciMethod *callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const {
// negative filter: should send NOT be inlined? returns NULL (--> inline) or rejection msg
if (!UseOldInlining) {
const char* fail = NULL;
@@ -269,14 +286,13 @@
}
const char *msg = NULL;
- if ((msg = shouldInline(callee_method, caller_method, caller_bci,
- profile, wci_result)) != NULL) {
+ msg = should_inline(callee_method, caller_method, caller_bci, profile, wci_result);
+ if (msg != NULL)
return msg;
- }
- if ((msg = shouldNotInline(callee_method, caller_method,
- wci_result)) != NULL) {
+
+ msg = should_not_inline(callee_method, caller_method, wci_result);
+ if (msg != NULL)
return msg;
- }
if (InlineAccessors && callee_method->is_accessor()) {
// accessor methods are not subject to any of the following limits.
@@ -492,9 +508,8 @@
new_depth_adjust -= 1; // don't count method handle calls from java.lang.invoke implem
}
if (new_depth_adjust != 0 && PrintInlining) {
- stringStream nm1; caller_jvms->method()->print_name(&nm1);
- stringStream nm2; callee_method->print_name(&nm2);
- tty->print_cr("discounting inlining depth from %s to %s", nm1.base(), nm2.base());
+ CompileTask::print_inline_indent(inline_depth());
+ tty->print_cr(" \\-> discounting inline depth");
}
if (new_depth_adjust != 0 && C->log()) {
int id1 = C->log()->identify(caller_jvms->method());
--- a/hotspot/src/share/vm/opto/doCall.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/opto/doCall.cpp Tue May 24 14:32:10 2011 -0700
@@ -62,7 +62,10 @@
CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual,
JVMState* jvms, bool allow_inline,
float prof_factor) {
- CallGenerator* cg;
+ CallGenerator* cg;
+ ciMethod* caller = jvms->method();
+ int bci = jvms->bci();
+ Bytecodes::Code bytecode = caller->java_code_at_bci(bci);
guarantee(call_method != NULL, "failed method resolution");
// Dtrace currently doesn't work unless all calls are vanilla
@@ -73,7 +76,7 @@
// Note: When we get profiling during stage-1 compiles, we want to pull
// from more specific profile data which pertains to this inlining.
// Right now, ignore the information in jvms->caller(), and do method[bci].
- ciCallProfile profile = jvms->method()->call_profile_at_bci(jvms->bci());
+ ciCallProfile profile = caller->call_profile_at_bci(bci);
// See how many times this site has been invoked.
int site_count = profile.count();
@@ -116,7 +119,7 @@
// MethodHandle.invoke* are native methods which obviously don't
// have bytecodes and so normal inlining fails.
if (call_method->is_method_handle_invoke()) {
- if (jvms->method()->java_code_at_bci(jvms->bci()) != Bytecodes::_invokedynamic) {
+ if (bytecode != Bytecodes::_invokedynamic) {
GraphKit kit(jvms);
Node* n = kit.argument(0);
@@ -125,17 +128,19 @@
ciObject* const_oop = oop_ptr->const_oop();
ciMethodHandle* method_handle = const_oop->as_method_handle();
- // Set the actually called method to have access to the class
- // and signature in the MethodHandleCompiler.
+ // Set the callee to have access to the class and signature in
+ // the MethodHandleCompiler.
method_handle->set_callee(call_method);
+ method_handle->set_caller(caller);
+ method_handle->set_call_profile(&profile);
// Get an adapter for the MethodHandle.
ciMethod* target_method = method_handle->get_method_handle_adapter();
- CallGenerator* hit_cg = NULL;
- if (target_method != NULL)
- hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor);
- if (hit_cg != NULL && hit_cg->is_inline())
- return hit_cg;
+ if (target_method != NULL) {
+ CallGenerator* hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor);
+ if (hit_cg != NULL && hit_cg->is_inline())
+ return hit_cg;
+ }
}
return CallGenerator::for_direct_call(call_method);
@@ -148,18 +153,20 @@
ciCallSite* call_site = str.get_call_site();
ciMethodHandle* method_handle = call_site->get_target();
- // Set the actually called method to have access to the class
- // and signature in the MethodHandleCompiler.
+ // Set the callee to have access to the class and signature in
+ // the MethodHandleCompiler.
method_handle->set_callee(call_method);
+ method_handle->set_caller(caller);
+ method_handle->set_call_profile(&profile);
// Get an adapter for the MethodHandle.
ciMethod* target_method = method_handle->get_invokedynamic_adapter();
- CallGenerator* hit_cg = NULL;
- if (target_method != NULL)
- hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor);
- if (hit_cg != NULL && hit_cg->is_inline()) {
- CallGenerator* miss_cg = CallGenerator::for_dynamic_call(call_method);
- return CallGenerator::for_predicted_dynamic_call(method_handle, miss_cg, hit_cg, prof_factor);
+ if (target_method != NULL) {
+ CallGenerator* hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor);
+ if (hit_cg != NULL && hit_cg->is_inline()) {
+ CallGenerator* miss_cg = CallGenerator::for_dynamic_call(call_method);
+ return CallGenerator::for_predicted_dynamic_call(method_handle, miss_cg, hit_cg, prof_factor);
+ }
}
// If something failed, generate a normal dynamic call.
--- a/hotspot/src/share/vm/opto/loopTransform.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/opto/loopTransform.cpp Tue May 24 14:32:10 2011 -0700
@@ -1230,7 +1230,7 @@
set_ctrl(new_limit, C->root());
} else {
// Limit is not constant.
- {
+ if (loop_head->unrolled_count() == 1) { // only for first unroll
// Separate limit by Opaque node in case it is an incremented
// variable from previous loop to avoid using pre-incremented
// value which could increase register pressure.
--- a/hotspot/src/share/vm/opto/parse.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/opto/parse.hpp Tue May 24 14:32:10 2011 -0700
@@ -68,8 +68,8 @@
JVMState* caller_jvms,
int caller_bci);
const char* try_to_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result);
- const char* shouldInline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const;
- const char* shouldNotInline(ciMethod* callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const;
+ const char* should_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const;
+ const char* should_not_inline(ciMethod* callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const;
void print_inlining(ciMethod *callee_method, int caller_bci, const char *failure_msg) const;
InlineTree *caller_tree() const { return _caller_tree; }
--- a/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/prims/jvmtiTagMap.cpp Tue May 24 14:32:10 2011 -0700
@@ -3158,6 +3158,9 @@
if (fr->is_entry_frame()) {
last_entry_frame = fr;
}
+ if (fr->is_ricochet_frame()) {
+ fr->oops_ricochet_do(blk, vf->register_map());
+ }
}
vf = vf->sender();
--- a/hotspot/src/share/vm/prims/methodHandleWalk.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/prims/methodHandleWalk.cpp Tue May 24 14:32:10 2011 -0700
@@ -31,6 +31,11 @@
* JSR 292 reference implementation: method handle structure analysis
*/
+#ifdef PRODUCT
+#define print_method_handle(mh) {}
+#else //PRODUCT
+extern "C" void print_method_handle(oop mh);
+#endif //PRODUCT
// -----------------------------------------------------------------------------
// MethodHandleChain
@@ -206,8 +211,10 @@
lose("bad argument index", CHECK_(empty));
}
+ bool retain_original_args = false; // used by fold/collect logic
+
// perform the adapter action
- switch (chain().adapter_conversion_op()) {
+ switch (conv_op) {
case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY:
// No changes to arguments; pass the bits through.
break;
@@ -216,51 +223,36 @@
// To keep the verifier happy, emit bitwise ("raw") conversions as needed.
// See MethodHandles::same_basic_type_for_arguments for allowed conversions.
Handle incoming_mtype(THREAD, chain().method_type_oop());
- oop outgoing_mh_oop = chain().vmtarget_oop();
- if (!java_lang_invoke_MethodHandle::is_instance(outgoing_mh_oop))
- lose("outgoing target not a MethodHandle", CHECK_(empty));
- Handle outgoing_mtype(THREAD, java_lang_invoke_MethodHandle::type(outgoing_mh_oop));
- outgoing_mh_oop = NULL; // GC safety
+ Handle outgoing_mtype;
+ {
+ oop outgoing_mh_oop = chain().vmtarget_oop();
+ if (!java_lang_invoke_MethodHandle::is_instance(outgoing_mh_oop))
+ lose("outgoing target not a MethodHandle", CHECK_(empty));
+ outgoing_mtype = Handle(THREAD, java_lang_invoke_MethodHandle::type(outgoing_mh_oop));
+ }
int nptypes = java_lang_invoke_MethodType::ptype_count(outgoing_mtype());
if (nptypes != java_lang_invoke_MethodType::ptype_count(incoming_mtype()))
lose("incoming and outgoing parameter count do not agree", CHECK_(empty));
+ // Argument types.
for (int i = 0, slot = _outgoing.length() - 1; slot >= 0; slot--) {
SlotState* arg_state = slot_state(slot);
if (arg_state->_type == T_VOID) continue;
- ArgToken arg = _outgoing.at(slot)._arg;
- klassOop in_klass = NULL;
- klassOop out_klass = NULL;
- BasicType inpbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(incoming_mtype(), i), &in_klass);
- BasicType outpbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(outgoing_mtype(), i), &out_klass);
- assert(inpbt == arg.basic_type(), "sanity");
-
- if (inpbt != outpbt) {
- vmIntrinsics::ID iid = vmIntrinsics::for_raw_conversion(inpbt, outpbt);
- if (iid == vmIntrinsics::_none) {
- lose("no raw conversion method", CHECK_(empty));
- }
- ArgToken arglist[2];
- arglist[0] = arg; // outgoing 'this'
- arglist[1] = ArgToken(); // sentinel
- arg = make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty));
- change_argument(inpbt, slot, outpbt, arg);
- }
-
+ klassOop src_klass = NULL;
+ klassOop dst_klass = NULL;
+ BasicType src = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(incoming_mtype(), i), &src_klass);
+ BasicType dst = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(outgoing_mtype(), i), &dst_klass);
+ retype_raw_argument_type(src, dst, slot, CHECK_(empty));
i++; // We need to skip void slots at the top of the loop.
}
- BasicType inrbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(incoming_mtype()));
- BasicType outrbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(outgoing_mtype()));
- if (inrbt != outrbt) {
- if (inrbt == T_INT && outrbt == T_VOID) {
- // See comments in MethodHandles::same_basic_type_for_arguments.
- } else {
- assert(false, "IMPLEMENT ME");
- lose("no raw conversion method", CHECK_(empty));
- }
+ // Return type.
+ {
+ BasicType src = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(incoming_mtype()));
+ BasicType dst = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(outgoing_mtype()));
+ retype_raw_return_type(src, dst, CHECK_(empty));
}
break;
}
@@ -273,7 +265,7 @@
assert(dest == arg_state->_type, "");
ArgToken arg = arg_state->_arg;
ArgToken new_arg = make_conversion(T_OBJECT, dest_klass, Bytecodes::_checkcast, arg, CHECK_(empty));
- assert(arg.index() == new_arg.index(), "should be the same index");
+ assert(arg.token_type() >= tt_symbolic || arg.index() == new_arg.index(), "should be the same index");
debug_only(dest_klass = (klassOop)badOop);
break;
}
@@ -332,7 +324,7 @@
ArgToken arglist[2];
arglist[0] = arg; // outgoing value
arglist[1] = ArgToken(); // sentinel
- arg = make_invoke(NULL, boxer, Bytecodes::_invokevirtual, false, 1, &arglist[0], CHECK_(empty));
+ arg = make_invoke(NULL, boxer, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty));
change_argument(src, arg_slot, T_OBJECT, arg);
break;
}
@@ -404,8 +396,54 @@
break;
}
- case java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS: { //NYI, may GC
- lose("unimplemented", CHECK_(empty));
+ case java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS:
+ retain_original_args = true; // and fall through:
+ case java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS: {
+ // call argument MH recursively
+ //{static int x; if (!x++) print_method_handle(chain().method_handle_oop()); --x;}
+ Handle recursive_mh(THREAD, chain().adapter_arg_oop());
+ if (!java_lang_invoke_MethodHandle::is_instance(recursive_mh())) {
+ lose("recursive target not a MethodHandle", CHECK_(empty));
+ }
+ Handle recursive_mtype(THREAD, java_lang_invoke_MethodHandle::type(recursive_mh()));
+ int argc = java_lang_invoke_MethodType::ptype_count(recursive_mtype());
+ int coll_slots = java_lang_invoke_MethodHandle::vmslots(recursive_mh());
+ BasicType rtype = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(recursive_mtype()));
+ ArgToken* arglist = NEW_RESOURCE_ARRAY(ArgToken, 1 + argc + 1); // 1+: mh, +1: sentinel
+ arglist[0] = make_oop_constant(recursive_mh(), CHECK_(empty));
+ if (arg_slot < 0 || coll_slots < 0 || arg_slot + coll_slots > _outgoing.length()) {
+ lose("bad fold/collect arg slot", CHECK_(empty));
+ }
+ for (int i = 0, slot = arg_slot + coll_slots - 1; slot >= arg_slot; slot--) {
+ SlotState* arg_state = slot_state(slot);
+ BasicType arg_type = arg_state->_type;
+ if (arg_type == T_VOID) continue;
+ ArgToken arg = _outgoing.at(slot)._arg;
+ if (i >= argc) { lose("bad fold/collect arg", CHECK_(empty)); }
+ arglist[1+i] = arg;
+ if (!retain_original_args)
+ change_argument(arg_type, slot, T_VOID, ArgToken(tt_void));
+ }
+ arglist[1+argc] = ArgToken(); // sentinel
+ oop invoker = java_lang_invoke_MethodTypeForm::vmlayout(
+ java_lang_invoke_MethodType::form(recursive_mtype()) );
+ if (invoker == NULL || !invoker->is_method()) {
+ lose("bad vmlayout slot", CHECK_(empty));
+ }
+ // FIXME: consider inlining the invokee at the bytecode level
+ ArgToken ret = make_invoke(methodOop(invoker), vmIntrinsics::_none,
+ Bytecodes::_invokevirtual, false, 1+argc, &arglist[0], CHECK_(empty));
+ DEBUG_ONLY(invoker = NULL);
+ if (rtype == T_OBJECT) {
+ klassOop rklass = java_lang_Class::as_klassOop( java_lang_invoke_MethodType::rtype(recursive_mtype()) );
+ if (rklass != SystemDictionary::Object_klass() &&
+ !Klass::cast(rklass)->is_interface()) {
+ // preserve type safety
+ ret = make_conversion(T_OBJECT, rklass, Bytecodes::_checkcast, ret, CHECK_(empty));
+ }
+ }
+ int ret_slot = arg_slot + (retain_original_args ? coll_slots : 0);
+ change_argument(T_VOID, ret_slot, rtype, ret);
break;
}
@@ -452,9 +490,18 @@
Bytecodes::_invokestatic, false, 3, &arglist[0], CHECK_(empty));
// Spread out the array elements.
- Bytecodes::Code aload_op = Bytecodes::_aaload;
- if (element_type != T_OBJECT) {
- lose("primitive array NYI", CHECK_(empty));
+ Bytecodes::Code aload_op = Bytecodes::_nop;
+ switch (element_type) {
+ case T_INT: aload_op = Bytecodes::_iaload; break;
+ case T_LONG: aload_op = Bytecodes::_laload; break;
+ case T_FLOAT: aload_op = Bytecodes::_faload; break;
+ case T_DOUBLE: aload_op = Bytecodes::_daload; break;
+ case T_OBJECT: aload_op = Bytecodes::_aaload; break;
+ case T_BOOLEAN: // fall through:
+ case T_BYTE: aload_op = Bytecodes::_baload; break;
+ case T_CHAR: aload_op = Bytecodes::_caload; break;
+ case T_SHORT: aload_op = Bytecodes::_saload; break;
+ default: lose("primitive array NYI", CHECK_(empty));
}
int ap = arg_slot;
for (int i = 0; i < spread_length; i++) {
@@ -467,11 +514,6 @@
break;
}
- case java_lang_invoke_AdapterMethodHandle::OP_FLYBY: //NYI, runs Java code
- case java_lang_invoke_AdapterMethodHandle::OP_RICOCHET: //NYI, runs Java code
- lose("unimplemented", CHECK_(empty));
- break;
-
default:
lose("bad adapter conversion", CHECK_(empty));
break;
@@ -495,7 +537,7 @@
lose("bad bound value", CHECK_(empty));
}
}
- debug_only(arg_oop = badOop);
+ DEBUG_ONLY(arg_oop = badOop);
change_argument(T_VOID, arg_slot, arg_type, arg);
}
@@ -538,11 +580,10 @@
}
for (int i = 0; i < nptypes; i++) {
klassOop arg_type_klass = NULL;
- BasicType arg_type = java_lang_Class::as_BasicType(
- java_lang_invoke_MethodType::ptype(mtype(), i), &arg_type_klass);
+ BasicType arg_type = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(mtype(), i), &arg_type_klass);
int index = new_local_index(arg_type);
ArgToken arg = make_parameter(arg_type, arg_type_klass, index, CHECK);
- debug_only(arg_type_klass = (klassOop) NULL);
+ DEBUG_ONLY(arg_type_klass = (klassOop) NULL);
_outgoing.at_put(argp, make_state(arg_type, arg));
if (type2size[arg_type] == 2) {
// add the extra slot, so we can model the JVM stack
@@ -552,8 +593,7 @@
}
// call make_parameter at the end of the list for the return type
klassOop ret_type_klass = NULL;
- BasicType ret_type = java_lang_Class::as_BasicType(
- java_lang_invoke_MethodType::rtype(mtype()), &ret_type_klass);
+ BasicType ret_type = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(mtype()), &ret_type_klass);
ArgToken ret = make_parameter(ret_type, ret_type_klass, -1, CHECK);
// ignore ret; client can catch it if needed
}
@@ -605,11 +645,54 @@
// -----------------------------------------------------------------------------
+// MethodHandleWalker::retype_raw_conversion
+//
+// Do the raw retype conversions for OP_RETYPE_RAW.
+void MethodHandleWalker::retype_raw_conversion(BasicType src, BasicType dst, bool for_return, int slot, TRAPS) {
+ if (src != dst) {
+ if (MethodHandles::same_basic_type_for_returns(src, dst, /*raw*/ true)) {
+ if (MethodHandles::is_float_fixed_reinterpretation_cast(src, dst)) {
+ if (for_return) Untested("MHW return raw conversion"); // still untested
+ vmIntrinsics::ID iid = vmIntrinsics::for_raw_conversion(src, dst);
+ if (iid == vmIntrinsics::_none) {
+ lose("no raw conversion method", CHECK);
+ }
+ ArgToken arglist[2];
+ if (!for_return) {
+ // argument type conversion
+ ArgToken arg = _outgoing.at(slot)._arg;
+ assert(arg.token_type() >= tt_symbolic || src == arg.basic_type(), "sanity");
+ arglist[0] = arg; // outgoing 'this'
+ arglist[1] = ArgToken(); // sentinel
+ arg = make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK);
+ change_argument(src, slot, dst, arg);
+ } else {
+ // return type conversion
+ klassOop arg_klass = NULL;
+ arglist[0] = make_parameter(src, arg_klass, -1, CHECK); // return value
+ arglist[1] = ArgToken(); // sentinel
+ (void) make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK);
+ }
+ } else {
+ // Nothing to do.
+ }
+ } else if (src == T_OBJECT && is_java_primitive(dst)) {
+ // ref-to-prim: discard ref, push zero
+ lose("requested ref-to-prim conversion not expected", CHECK);
+ } else {
+ lose("requested raw conversion not allowed", CHECK);
+ }
+ }
+}
+
+
+// -----------------------------------------------------------------------------
// MethodHandleCompiler
-MethodHandleCompiler::MethodHandleCompiler(Handle root, methodHandle callee, bool is_invokedynamic, TRAPS)
+MethodHandleCompiler::MethodHandleCompiler(Handle root, methodHandle callee, int invoke_count, bool is_invokedynamic, TRAPS)
: MethodHandleWalker(root, is_invokedynamic, THREAD),
_callee(callee),
+ _invoke_count(invoke_count),
_thread(THREAD),
_bytecode(THREAD, 50),
_constants(THREAD, 10),
@@ -709,6 +792,7 @@
case Bytecodes::_astore_1:
case Bytecodes::_astore_2:
case Bytecodes::_astore_3:
+ case Bytecodes::_iand:
case Bytecodes::_i2l:
case Bytecodes::_i2f:
case Bytecodes::_i2d:
@@ -935,7 +1019,11 @@
break;
default:
- ShouldNotReachHere();
+ if (op == Bytecodes::_illegal)
+ lose("no such primitive conversion", THREAD);
+ else
+ lose("bad primitive conversion op", THREAD);
+ return make_prim_constant(type, &zero_jvalue, THREAD);
}
return make_parameter(type, tk, index, THREAD);
@@ -946,7 +1034,9 @@
// MethodHandleCompiler
//
-static jvalue zero_jvalue;
+// Values used by the compiler.
+jvalue MethodHandleCompiler::zero_jvalue = { 0 };
+jvalue MethodHandleCompiler::one_jvalue = { 1 };
// Emit bytecodes for the given invoke instruction.
MethodHandleWalker::ArgToken
@@ -954,18 +1044,18 @@
Bytecodes::Code op, bool tailcall,
int argc, MethodHandleWalker::ArgToken* argv,
TRAPS) {
+ ArgToken zero;
if (m == NULL) {
// Get the intrinsic methodOop.
m = vmIntrinsics::method_for(iid);
if (m == NULL) {
- ArgToken zero;
lose(vmIntrinsics::name_at(iid), CHECK_(zero));
}
}
- klassOop klass = m->method_holder();
- Symbol* name = m->name();
- Symbol* signature = m->signature();
+ klassOop klass = m->method_holder();
+ Symbol* name = m->name();
+ Symbol* signature = m->signature();
if (tailcall) {
// Actually, in order to make these methods more recognizable,
@@ -1031,7 +1121,6 @@
if (rbt != _rtype) {
if (rbt == T_VOID) {
// push a zero of the right sort
- ArgToken zero;
if (_rtype == T_OBJECT) {
zero = make_oop_constant(NULL, CHECK_(zero));
} else {
@@ -1041,9 +1130,27 @@
} else if (_rtype == T_VOID) {
// We'll emit a _return with something on the stack.
// It's OK to ignore what's on the stack.
+ } else if (rbt == T_INT && is_subword_type(_rtype)) {
+ // Convert value to match return type.
+ switch (_rtype) {
+ case T_BOOLEAN: {
+ // boolean is treated as a one-bit unsigned integer.
+ // Cf. API documentation: java/lang/invoke/MethodHandles.html#explicitCastArguments
+ ArgToken one = make_prim_constant(T_INT, &one_jvalue, CHECK_(zero));
+ emit_load_constant(one);
+ emit_bc(Bytecodes::_iand);
+ break;
+ }
+ case T_BYTE: emit_bc(Bytecodes::_i2b); break;
+ case T_CHAR: emit_bc(Bytecodes::_i2c); break;
+ case T_SHORT: emit_bc(Bytecodes::_i2s); break;
+ default: ShouldNotReachHere();
+ }
+ } else if (is_subword_type(rbt) && (is_subword_type(_rtype) || (_rtype == T_INT))) {
+ // The subword type was returned as an int and will be passed
+ // on as an int.
} else {
- tty->print_cr("*** rbt=%d != rtype=%d", rbt, _rtype);
- assert(false, "IMPLEMENT ME");
+ lose("unknown conversion", CHECK_(zero));
}
}
switch (_rtype) {
@@ -1173,7 +1280,7 @@
methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
- methodHandle nullHandle;
+ methodHandle empty;
// Create a method that holds the generated bytecode. invokedynamic
// has no receiver, normal MH calls do.
int flags_bits;
@@ -1182,13 +1289,16 @@
else
flags_bits = (/*JVM_MH_INVOKE_BITS |*/ JVM_ACC_PUBLIC | JVM_ACC_FINAL | JVM_ACC_SYNTHETIC);
- methodOop m_oop = oopFactory::new_method(bytecode_length(),
- accessFlags_from(flags_bits),
- 0, 0, 0, oopDesc::IsSafeConc, CHECK_(nullHandle));
- methodHandle m(THREAD, m_oop);
- m_oop = NULL; // oop not GC safe
+ // Create a new method
+ methodHandle m;
+ {
+ methodOop m_oop = oopFactory::new_method(bytecode_length(),
+ accessFlags_from(flags_bits),
+ 0, 0, 0, oopDesc::IsSafeConc, CHECK_(empty));
+ m = methodHandle(THREAD, m_oop);
+ }
- constantPoolHandle cpool = get_constant_pool(CHECK_(nullHandle));
+ constantPoolHandle cpool = get_constant_pool(CHECK_(empty));
m->set_constants(cpool());
m->set_name_index(_name_index);
@@ -1203,16 +1313,34 @@
typeArrayHandle exception_handlers(THREAD, Universe::the_empty_int_array());
m->set_exception_table(exception_handlers());
- // Set the carry bit of the invocation counter to force inlining of
- // the adapter.
- InvocationCounter* ic = m->invocation_counter();
- ic->set_carry_flag();
-
// Rewrite the method and set up the constant pool cache.
- objArrayOop m_array = oopFactory::new_system_objArray(1, CHECK_(nullHandle));
+ objArrayOop m_array = oopFactory::new_system_objArray(1, CHECK_(empty));
objArrayHandle methods(THREAD, m_array);
methods->obj_at_put(0, m());
- Rewriter::rewrite(_target_klass(), cpool, methods, CHECK_(nullHandle)); // Use fake class.
+ Rewriter::rewrite(_target_klass(), cpool, methods, CHECK_(empty)); // Use fake class.
+
+ // Set the invocation counter's count to the invoke count of the
+ // original call site.
+ InvocationCounter* ic = m->invocation_counter();
+ ic->set(InvocationCounter::wait_for_compile, _invoke_count);
+
+ // Create a new MDO
+ {
+ methodDataOop mdo = oopFactory::new_methodData(m, CHECK_(empty));
+ assert(m->method_data() == NULL, "there should not be an MDO yet");
+ m->set_method_data(mdo);
+
+ // Iterate over all profile data and set the count of the counter
+ // data entries to the original call site counter.
+ for (ProfileData* profile_data = mdo->first_data();
+ mdo->is_valid(profile_data);
+ profile_data = mdo->next_data(profile_data)) {
+ if (profile_data->is_CounterData()) {
+ CounterData* counter_data = profile_data->as_CounterData();
+ counter_data->set_count(_invoke_count);
+ }
+ }
+ }
#ifndef PRODUCT
if (TraceMethodHandles) {
@@ -1228,7 +1356,6 @@
#ifndef PRODUCT
-#if 0
// MH printer for debugging.
class MethodHandlePrinter : public MethodHandleWalker {
@@ -1236,6 +1363,7 @@
outputStream* _out;
bool _verbose;
int _temp_num;
+ int _param_state;
stringStream _strbuf;
const char* strbuf() {
const char* s = _strbuf.as_string();
@@ -1243,14 +1371,21 @@
return s;
}
ArgToken token(const char* str) {
- return (ArgToken) str;
+ jvalue string_con;
+ string_con.j = (intptr_t) str;
+ return ArgToken(tt_symbolic, T_LONG, string_con);
+ }
+ const char* string(ArgToken token) {
+ return (const char*) (intptr_t) token.get_jlong();
}
void start_params() {
+ _param_state <<= 1;
_out->print("(");
}
void end_params() {
if (_verbose) _out->print("\n");
_out->print(") => {");
+ _param_state >>= 1;
}
void put_type_name(BasicType type, klassOop tk, outputStream* s) {
const char* kname = NULL;
@@ -1270,9 +1405,10 @@
public:
MethodHandlePrinter(Handle root, bool verbose, outputStream* out, TRAPS)
- : MethodHandleWalker(root, THREAD),
+ : MethodHandleWalker(root, false, THREAD),
_out(out),
_verbose(verbose),
+ _param_state(0),
_temp_num(0)
{
start_params();
@@ -1280,9 +1416,10 @@
virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) {
if (argnum < 0) {
end_params();
- return NULL;
+ return token("return");
}
- if (argnum == 0) {
+ if ((_param_state & 1) == 0) {
+ _param_state |= 1;
_out->print(_verbose ? "\n " : "");
} else {
_out->print(_verbose ? ",\n " : ", ");
@@ -1312,8 +1449,15 @@
java_lang_boxing_object::print(type, con, &_strbuf);
return maybe_make_temp("constant", type, "k");
}
- virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken src, TRAPS) {
- _strbuf.print("%s(%s", Bytecodes::name(op), (const char*)src);
+ void print_bytecode_name(Bytecodes::Code op) {
+ if (Bytecodes::is_defined(op))
+ _strbuf.print("%s", Bytecodes::name(op));
+ else
+ _strbuf.print("bytecode_%d", (int) op);
+ }
+ virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS) {
+ print_bytecode_name(op);
+ _strbuf.print("(%s", string(src));
if (tk != NULL) {
_strbuf.print(", ");
put_type_name(type, tk, &_strbuf);
@@ -1321,8 +1465,8 @@
_strbuf.print(")");
return maybe_make_temp("convert", type, "v");
}
- virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken base, ArgToken offset, TRAPS) {
- _strbuf.print("%s(%s, %s", Bytecodes::name(op), (const char*)base, (const char*)offset);
+ virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS) {
+ _strbuf.print("%s(%s, %s", Bytecodes::name(op), string(base), string(offset));
if (tk != NULL) {
_strbuf.print(", ");
put_type_name(type, tk, &_strbuf);
@@ -1333,7 +1477,8 @@
virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid,
Bytecodes::Code op, bool tailcall,
int argc, ArgToken* argv, TRAPS) {
- Symbol* name, sig;
+ Symbol* name;
+ Symbol* sig;
if (m != NULL) {
name = m->name();
sig = m->signature();
@@ -1343,7 +1488,7 @@
}
_strbuf.print("%s %s%s(", Bytecodes::name(op), name->as_C_string(), sig->as_C_string());
for (int i = 0; i < argc; i++) {
- _strbuf.print("%s%s", (i > 0 ? ", " : ""), (const char*)argv[i]);
+ _strbuf.print("%s%s", (i > 0 ? ", " : ""), string(argv[i]));
}
_strbuf.print(")");
if (!tailcall) {
@@ -1381,24 +1526,20 @@
if (HAS_PENDING_EXCEPTION) {
oop ex = PENDING_EXCEPTION;
CLEAR_PENDING_EXCEPTION;
- out->print("\n*** ");
- if (ex != Universe::virtual_machine_error_instance())
- ex->print_on(out);
- else
- out->print("lose: %s", printer.lose_message());
- out->print("\n}\n");
+ out->print(" *** ");
+ if (printer.lose_message() != NULL) out->print("%s ", printer.lose_message());
+ out->print("}");
}
out->print("\n");
}
};
-#endif // 0
extern "C"
void print_method_handle(oop mh) {
if (!mh->is_oop()) {
- tty->print_cr("*** not a method handle: "INTPTR_FORMAT, (intptr_t)mh);
+ tty->print_cr("*** not a method handle: "PTR_FORMAT, (intptr_t)mh);
} else if (java_lang_invoke_MethodHandle::is_instance(mh)) {
- //MethodHandlePrinter::print(mh);
+ MethodHandlePrinter::print(mh);
} else {
tty->print("*** not a method handle: ");
mh->print();
--- a/hotspot/src/share/vm/prims/methodHandleWalk.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/prims/methodHandleWalk.hpp Tue May 24 14:32:10 2011 -0700
@@ -113,6 +113,7 @@
tt_parameter,
tt_temporary,
tt_constant,
+ tt_symbolic,
tt_illegal
};
@@ -164,6 +165,10 @@
bool _for_invokedynamic;
int _local_index;
+ // This array is kept in an unusual order, indexed by low-level "slot number".
+ // TOS is always _outgoing.at(0), so simple pushes and pops shift the whole _outgoing array.
+ // If there is a receiver in the current argument list, it is at _outgoing.at(_outgoing.length()-1).
+ // If a value at _outgoing.at(n) is T_LONG or T_DOUBLE, the value at _outgoing.at(n+1) is T_VOID.
GrowableArray<SlotState> _outgoing; // current outgoing parameter slots
int _outgoing_argc; // # non-empty outgoing slots
@@ -173,6 +178,11 @@
// Insert or delete a second empty slot as needed.
void change_argument(BasicType old_type, int slot, BasicType new_type, const ArgToken& new_arg);
+ // Raw retype conversions for OP_RAW_RETYPE.
+ void retype_raw_conversion(BasicType src, BasicType dst, bool for_return, int slot, TRAPS);
+ void retype_raw_argument_type(BasicType src, BasicType dst, int slot, TRAPS) { retype_raw_conversion(src, dst, false, slot, CHECK); }
+ void retype_raw_return_type( BasicType src, BasicType dst, TRAPS) { retype_raw_conversion(src, dst, true, -1, CHECK); }
+
SlotState* slot_state(int slot) {
if (slot < 0 || slot >= _outgoing.length())
return NULL;
@@ -221,12 +231,12 @@
int max_locals() const { return _local_index; }
// plug-in abstract interpretation steps:
- virtual ArgToken make_parameter( BasicType type, klassOop tk, int argnum, TRAPS ) = 0;
- virtual ArgToken make_prim_constant( BasicType type, jvalue* con, TRAPS ) = 0;
- virtual ArgToken make_oop_constant( oop con, TRAPS ) = 0;
- virtual ArgToken make_conversion( BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS ) = 0;
- virtual ArgToken make_fetch( BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS ) = 0;
- virtual ArgToken make_invoke( methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS ) = 0;
+ virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) = 0;
+ virtual ArgToken make_prim_constant(BasicType type, jvalue* con, TRAPS) = 0;
+ virtual ArgToken make_oop_constant(oop con, TRAPS) = 0;
+ virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS) = 0;
+ virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS) = 0;
+ virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS) = 0;
// For make_invoke, the methodOop can be NULL if the intrinsic ID
// is something other than vmIntrinsics::_none.
@@ -247,11 +257,16 @@
class MethodHandleCompiler : public MethodHandleWalker {
private:
methodHandle _callee;
+ int _invoke_count; // count the original call site has been executed
KlassHandle _rklass; // Return type for casting.
BasicType _rtype;
KlassHandle _target_klass;
Thread* _thread;
+ // Values used by the compiler.
+ static jvalue zero_jvalue;
+ static jvalue one_jvalue;
+
// Fake constant pool entry.
class ConstantValue {
private:
@@ -416,7 +431,7 @@
methodHandle get_method_oop(TRAPS) const;
public:
- MethodHandleCompiler(Handle root, methodHandle call_method, bool for_invokedynamic, TRAPS);
+ MethodHandleCompiler(Handle root, methodHandle callee, int invoke_count, bool for_invokedynamic, TRAPS);
// Compile the given MH chain into bytecode.
methodHandle compile(TRAPS);
--- a/hotspot/src/share/vm/prims/methodHandles.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/prims/methodHandles.cpp Tue May 24 14:32:10 2011 -0700
@@ -66,8 +66,8 @@
"adapter_drop_args",
"adapter_collect_args",
"adapter_spread_args",
- "adapter_flyby",
- "adapter_ricochet",
+ "adapter_fold_args",
+ "adapter_unused_13",
// optimized adapter types:
"adapter_swap_args/1",
@@ -83,9 +83,76 @@
"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",
+
+ // return value handlers for collect/filter/fold adapters:
+ "return/ref",
+ "return/int",
+ "return/long",
+ "return/float",
+ "return/double",
+ "return/void",
+ "return/S0/ref",
+ "return/S1/ref",
+ "return/S2/ref",
+ "return/S3/ref",
+ "return/S4/ref",
+ "return/S5/ref",
+ "return/any",
+
+ // spreading (array length cases 0, 1, ...)
+ "adapter_spread/0",
+ "adapter_spread/1/ref",
+ "adapter_spread/2/ref",
+ "adapter_spread/3/ref",
+ "adapter_spread/4/ref",
+ "adapter_spread/5/ref",
+ "adapter_spread/ref",
+ "adapter_spread/byte",
+ "adapter_spread/char",
+ "adapter_spread/short",
+ "adapter_spread/int",
+ "adapter_spread/long",
+ "adapter_spread/float",
+ "adapter_spread/double",
+
+ // blocking filter/collect conversions:
+ "adapter_collect/ref",
+ "adapter_collect/int",
+ "adapter_collect/long",
+ "adapter_collect/float",
+ "adapter_collect/double",
+ "adapter_collect/void",
+ "adapter_collect/0/ref",
+ "adapter_collect/1/ref",
+ "adapter_collect/2/ref",
+ "adapter_collect/3/ref",
+ "adapter_collect/4/ref",
+ "adapter_collect/5/ref",
+ "adapter_filter/S0/ref",
+ "adapter_filter/S1/ref",
+ "adapter_filter/S2/ref",
+ "adapter_filter/S3/ref",
+ "adapter_filter/S4/ref",
+ "adapter_filter/S5/ref",
+ "adapter_collect/2/S0/ref",
+ "adapter_collect/2/S1/ref",
+ "adapter_collect/2/S2/ref",
+ "adapter_collect/2/S3/ref",
+ "adapter_collect/2/S4/ref",
+ "adapter_collect/2/S5/ref",
+
+ // blocking fold conversions:
+ "adapter_fold/ref",
+ "adapter_fold/int",
+ "adapter_fold/long",
+ "adapter_fold/float",
+ "adapter_fold/double",
+ "adapter_fold/void",
+ "adapter_fold/1/ref",
+ "adapter_fold/2/ref",
+ "adapter_fold/3/ref",
+ "adapter_fold/4/ref",
+ "adapter_fold/5/ref",
NULL
};
@@ -96,13 +163,23 @@
jobject MethodHandles::_raise_exception_method;
+address MethodHandles::_adapter_return_handlers[CONV_TYPE_MASK+1];
+
#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_fold_args), "adapter_fold_args"), "");
assert(!strcmp(entry_name(_adapter_opt_unboxi), "adapter_ref_to_prim/unboxi"), "");
+ assert(!strcmp(entry_name(_adapter_opt_spread_char), "adapter_spread/char"), "");
+ assert(!strcmp(entry_name(_adapter_opt_spread_double), "adapter_spread/double"), "");
+ assert(!strcmp(entry_name(_adapter_opt_collect_int), "adapter_collect/int"), "");
+ assert(!strcmp(entry_name(_adapter_opt_collect_0_ref), "adapter_collect/0/ref"), "");
+ assert(!strcmp(entry_name(_adapter_opt_collect_2_S3_ref), "adapter_collect/2/S3/ref"), "");
+ assert(!strcmp(entry_name(_adapter_opt_filter_S5_ref), "adapter_filter/S5/ref"), "");
+ assert(!strcmp(entry_name(_adapter_opt_fold_3_ref), "adapter_fold/3/ref"), "");
+ assert(!strcmp(entry_name(_adapter_opt_fold_void), "adapter_fold/void"), "");
return true;
}
#endif
@@ -112,6 +189,9 @@
// MethodHandles::generate_adapters
//
void MethodHandles::generate_adapters() {
+#ifdef TARGET_ARCH_NYI_6939861
+ if (FLAG_IS_DEFAULT(UseRicochetFrames)) UseRicochetFrames = false;
+#endif
if (!EnableInvokeDynamic || SystemDictionary::MethodHandle_klass() == NULL) return;
assert(_adapter_code == NULL, "generate only once");
@@ -126,7 +206,6 @@
g.generate();
}
-
//------------------------------------------------------------------------------
// MethodHandlesAdapterGenerator::generate
//
@@ -135,12 +214,62 @@
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);
+ if (MethodHandles::ek_supported(ek)) {
+ StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek));
+ MethodHandles::generate_method_handle_stub(_masm, ek);
+ }
}
}
+#ifdef TARGET_ARCH_NYI_6939861
+// these defs belong in methodHandles_<arch>.cpp
+frame MethodHandles::ricochet_frame_sender(const frame& fr, RegisterMap *map) {
+ ShouldNotCallThis();
+ return fr;
+}
+void MethodHandles::ricochet_frame_oops_do(const frame& fr, OopClosure* f, const RegisterMap* reg_map) {
+ ShouldNotCallThis();
+}
+#endif //TARGET_ARCH_NYI_6939861
+
+
+//------------------------------------------------------------------------------
+// MethodHandles::ek_supported
+//
+bool MethodHandles::ek_supported(MethodHandles::EntryKind ek) {
+ MethodHandles::EntryKind ek_orig = MethodHandles::ek_original_kind(ek);
+ switch (ek_orig) {
+ case _adapter_unused_13:
+ return false; // not defined yet
+ case _adapter_prim_to_ref:
+ return UseRicochetFrames && conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF);
+ case _adapter_collect_args:
+ return UseRicochetFrames && conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS);
+ case _adapter_fold_args:
+ return UseRicochetFrames && conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS);
+ case _adapter_opt_return_any:
+ return UseRicochetFrames;
+#ifdef TARGET_ARCH_NYI_6939861
+ // ports before 6939861 supported only three kinds of spread ops
+ case _adapter_spread_args:
+ // restrict spreads to three kinds:
+ switch (ek) {
+ case _adapter_opt_spread_0:
+ case _adapter_opt_spread_1:
+ case _adapter_opt_spread_more:
+ break;
+ default:
+ return false;
+ break;
+ }
+ break;
+#endif //TARGET_ARCH_NYI_6939861
+ }
+ return true;
+}
+
+
void MethodHandles::set_enabled(bool z) {
if (_enabled != z) {
guarantee(z && EnableInvokeDynamic, "can only enable once, and only if -XX:+EnableInvokeDynamic");
@@ -970,6 +1099,14 @@
return Klass::cast(SystemDictionary::Object_klass())->java_mirror();
}
+bool MethodHandles::is_float_fixed_reinterpretation_cast(BasicType src, BasicType dst) {
+ 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;
+}
+
bool MethodHandles::same_basic_type_for_arguments(BasicType src,
BasicType dst,
bool raw,
@@ -996,10 +1133,8 @@
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;
+ if (is_float_fixed_reinterpretation_cast(src, dst))
+ return true;
return false;
}
@@ -1270,7 +1405,7 @@
int argnum,
bool raw) {
const char* err = NULL;
- bool for_return = (argnum < 0);
+ const bool for_return = (argnum < 0);
// just in case:
if (src_type == T_ARRAY) src_type = T_OBJECT;
@@ -1279,17 +1414,17 @@
// Produce some nice messages if VerifyMethodHandles is turned on:
if (!same_basic_type_for_arguments(src_type, dst_type, raw, for_return)) {
if (src_type == T_OBJECT) {
- if (raw && dst_type == T_INT && is_always_null_type(src_klass))
- return NULL; // OK to convert a null pointer to a garbage int
- err = ((argnum >= 0)
+ if (raw && is_java_primitive(dst_type))
+ return NULL; // ref-to-prim discards ref and returns zero
+ err = (!for_return
? "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)
+ err = (!for_return
? "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)
+ err = (!for_return
? "type mismatch: passing a %s for method argument #%d, which expects %s"
: "type mismatch: returning a %s, but caller expects %s");
}
@@ -1298,11 +1433,11 @@
if (!class_cast_needed(dst_klass, src_klass)) {
if (raw)
return NULL; // reverse cast is OK; the MH target is trusted to enforce it
- err = ((argnum >= 0)
+ err = (!for_return
? "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)
+ err = (!for_return
? "reference mismatch: passing a %s for method argument #%d, which expects %s"
: "reference mismatch: returning a %s, but caller expects %s");
}
@@ -1323,7 +1458,7 @@
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) {
+ if (!for_return) {
assert(strstr(err, "%d") != NULL, "");
jio_snprintf(msg, msglen, err, src_name, argnum, dst_name);
} else {
@@ -1564,6 +1699,8 @@
if (m->is_abstract()) { THROW(vmSymbols::java_lang_AbstractMethodError()); }
java_lang_invoke_MethodHandle::init_vmslots(mh());
+ int vmargslot = m->size_of_parameters() - 1;
+ assert(java_lang_invoke_BoundMethodHandle::vmargslot(mh()) == vmargslot, "");
if (VerifyMethodHandles) {
verify_BoundMethodHandle_with_receiver(mh, m, CHECK);
@@ -1642,14 +1779,9 @@
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 target_pushes = decode_MethodHandle_stack_pushes(target());
assert(this_pushes == slots_pushed + target_pushes, "BMH stack motion must be correct");
- // do not blow the stack; use a Java-based adapter if this limit is exceeded
- // FIXME
- // if (slots_pushed + target_pushes > MethodHandlePushLimit)
- // err = "too many bound parameters";
}
}
@@ -1672,10 +1804,11 @@
}
java_lang_invoke_MethodHandle::init_vmslots(mh());
+ int argslot = java_lang_invoke_BoundMethodHandle::vmargslot(mh());
if (VerifyMethodHandles) {
int insert_after = argnum - 1;
- verify_vmargslot(mh, insert_after, java_lang_invoke_BoundMethodHandle::vmargslot(mh()), CHECK);
+ verify_vmargslot(mh, insert_after, argslot, CHECK);
verify_vmslots(mh, CHECK);
}
@@ -1769,6 +1902,7 @@
Handle target(THREAD, java_lang_invoke_AdapterMethodHandle::vmtarget(mh()));
Handle src_mtype(THREAD, java_lang_invoke_MethodHandle::type(mh()));
Handle dst_mtype(THREAD, java_lang_invoke_MethodHandle::type(target()));
+ Handle arg_mtype;
const char* err = NULL;
@@ -1777,25 +1911,29 @@
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) {
+ if (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<Object[]>"; break; }
- }
+ if (!ak->oop_is_array())
+ { err = "spread adapter requires argument representing an array class"; break; }
+ BasicType et = arrayKlass::cast(ak->as_klassOop())->element_type();
+ if (et != dest && stack_move <= 0)
+ { err = "spread adapter requires array class argument of correct type"; break; }
}
break;
- case _adapter_flyby:
- case _adapter_ricochet:
+ case _adapter_prim_to_ref: // boxer MH to use
+ case _adapter_collect_args: // method handle which collects the args
+ case _adapter_fold_args: // method handle which collects the args
+ if (!UseRicochetFrames) {
+ { err = "box/collect/fold operators are not supported"; break; }
+ }
if (!java_lang_invoke_MethodHandle::is_instance(argument()))
{ err = "MethodHandle adapter argument required"; break; }
+ arg_mtype = Handle(THREAD, java_lang_invoke_MethodHandle::type(argument()));
break;
default:
if (argument.not_null())
@@ -1806,6 +1944,7 @@
if (err == NULL) {
// Check that the src/dest types are supplied if needed.
+ // Also check relevant parameter or return types.
switch (ek) {
case _adapter_check_cast:
if (src != T_OBJECT || dest != T_OBJECT) {
@@ -1828,8 +1967,7 @@
}
break;
case _adapter_prim_to_ref:
- if (!is_java_primitive(src) || dest != T_OBJECT
- || argument() != Klass::cast(SystemDictionary::box_klass(src))->java_mirror()) {
+ if (!is_java_primitive(src) || dest != T_OBJECT) {
err = "adapter requires primitive src conversion subfield"; break;
}
break;
@@ -1840,14 +1978,12 @@
err = "adapter requires src/dest conversion subfields for swap"; break;
}
int swap_size = type2size[src];
- oop src_mtype = java_lang_invoke_AdapterMethodHandle::type(mh());
- oop dest_mtype = java_lang_invoke_AdapterMethodHandle::type(target());
- int slot_limit = java_lang_invoke_AdapterMethodHandle::vmslots(target());
+ int slot_limit = java_lang_invoke_MethodHandle::vmslots(target());
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);
+ int dest_arg = argument_slot_to_argnum(dst_mtype(), dest_slot);
verify_vmargslot(mh, dest_arg, dest_slot, CHECK);
if (!(dest_slot >= src_slot + swap_size) &&
!(src_slot >= dest_slot + swap_size)) {
@@ -1855,8 +1991,8 @@
} 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_lang_invoke_MethodType::ptype(src_mtype, dest_arg),
- java_lang_invoke_MethodType::ptype(dest_mtype, src_arg),
+ err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), dest_arg),
+ java_lang_invoke_MethodType::ptype(dst_mtype(), src_arg),
dest_arg);
} else if (ek == _adapter_rot_args) {
if (rotate_up) {
@@ -1864,8 +2000,8 @@
// 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_lang_invoke_MethodType::ptype(src_mtype, i),
- java_lang_invoke_MethodType::ptype(dest_mtype, i-1),
+ err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), i),
+ java_lang_invoke_MethodType::ptype(dst_mtype(), i-1),
i);
}
} else { // rotate down
@@ -1873,28 +2009,54 @@
// 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_lang_invoke_MethodType::ptype(src_mtype, i),
- java_lang_invoke_MethodType::ptype(dest_mtype, i+1),
+ err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), i),
+ java_lang_invoke_MethodType::ptype(dst_mtype(), i+1),
i);
}
}
}
if (err == NULL)
- err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype, src_arg),
- java_lang_invoke_MethodType::ptype(dest_mtype, dest_arg),
+ err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), src_arg),
+ java_lang_invoke_MethodType::ptype(dst_mtype(), dest_arg),
src_arg);
}
break;
+ case _adapter_spread_args:
case _adapter_collect_args:
- case _adapter_spread_args:
+ case _adapter_fold_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
+ bool is_spread = (ek == _adapter_spread_args);
+ bool is_fold = (ek == _adapter_fold_args);
+ BasicType coll_type = is_spread ? src : dest;
+ BasicType elem_type = is_spread ? dest : src;
+ // coll_type is type of args in collected form (or T_VOID if none)
+ // elem_type is common type of args in spread form (or T_VOID if missing or heterogeneous)
+ if (coll_type == 0 || elem_type == 0) {
+ err = "adapter requires src/dest subfields for spread or collect"; break;
+ }
+ if (is_spread && coll_type != T_OBJECT) {
+ err = "spread adapter requires object type for argument bundle"; break;
+ }
+ Handle spread_mtype = (is_spread ? dst_mtype : src_mtype);
+ int spread_slot = argslot;
+ int spread_arg = argnum;
+ int slots_pushed = stack_move / stack_move_unit();
+ int coll_slot_count = type2size[coll_type];
+ int spread_slot_count = (is_spread ? slots_pushed : -slots_pushed) + coll_slot_count;
+ if (is_fold) spread_slot_count = argument_slot_count(arg_mtype());
+ if (!is_spread) {
+ int init_slots = argument_slot_count(src_mtype());
+ int coll_slots = argument_slot_count(arg_mtype());
+ if (spread_slot_count > init_slots ||
+ spread_slot_count != coll_slots) {
+ err = "collect adapter has inconsistent arg counts"; break;
+ }
+ int next_slots = argument_slot_count(dst_mtype());
+ int unchanged_slots_in = (init_slots - spread_slot_count);
+ int unchanged_slots_out = (next_slots - coll_slot_count - (is_fold ? spread_slot_count : 0));
+ if (unchanged_slots_in != unchanged_slots_out) {
+ err = "collect adapter continuation has inconsistent arg counts"; break;
+ }
}
}
break;
@@ -1929,8 +2091,9 @@
}
break;
case _adapter_collect_args:
- if (slots_pushed > 1) {
- err = "adapter requires conversion subfield slots_pushed <= 1";
+ case _adapter_fold_args:
+ if (slots_pushed > 2) {
+ err = "adapter requires conversion subfield slots_pushed <= 2";
}
break;
case _adapter_spread_args:
@@ -1950,32 +2113,36 @@
}
if (err == NULL) {
- // Make sure this adapter does not push too deeply.
+ // Make sure this adapter's stack pushing is accurately recorded.
int slots_pushed = stack_move / stack_move_unit();
int this_vmslots = java_lang_invoke_MethodHandle::vmslots(mh());
int target_vmslots = java_lang_invoke_MethodHandle::vmslots(target());
+ int target_pushes = decode_MethodHandle_stack_pushes(target());
if (slots_pushed != (target_vmslots - this_vmslots)) {
err = "stack_move inconsistent with previous and current MethodType vmslots";
- } else if (slots_pushed > 0) {
- // verify stack_move against MethodHandlePushLimit
- int target_pushes = decode_MethodHandle_stack_pushes(target());
- // do not blow the stack; use a Java-based adapter if this limit is exceeded
- if (slots_pushed + target_pushes > MethodHandlePushLimit) {
- err = "adapter pushes too many parameters";
+ } else {
+ int this_pushes = decode_MethodHandle_stack_pushes(mh());
+ if (slots_pushed + target_pushes != this_pushes) {
+ if (this_pushes == 0)
+ err = "adapter push count not initialized";
+ else
+ err = "adapter push count is wrong";
}
}
// While we're at it, check that the stack motion decoder works:
- DEBUG_ONLY(int target_pushes = decode_MethodHandle_stack_pushes(target()));
DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh()));
assert(this_pushes == slots_pushed + target_pushes, "AMH stack motion must be correct");
}
if (err == NULL && vminfo != 0) {
switch (ek) {
- case _adapter_swap_args:
- case _adapter_rot_args:
- break; // OK
+ case _adapter_swap_args:
+ case _adapter_rot_args:
+ case _adapter_prim_to_ref:
+ case _adapter_collect_args:
+ case _adapter_fold_args:
+ break; // OK
default:
err = "vminfo subfield is reserved to the JVM";
}
@@ -2019,13 +2186,15 @@
}
void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS) {
- int argslot = java_lang_invoke_AdapterMethodHandle::vmargslot(mh());
- jint conversion = java_lang_invoke_AdapterMethodHandle::conversion(mh());
- jint conv_op = adapter_conversion_op(conversion);
+ Handle argument = java_lang_invoke_AdapterMethodHandle::argument(mh());
+ int argslot = java_lang_invoke_AdapterMethodHandle::vmargslot(mh());
+ jint conversion = java_lang_invoke_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
+ EntryKind ek_try; // temp
// Finalize the vmtarget field (Java initialized it to null).
if (!java_lang_invoke_MethodHandle::is_instance(target())) {
@@ -2034,17 +2203,23 @@
}
java_lang_invoke_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
+ int slots_pushed = stack_move / stack_move_unit();
+
+ if (VerifyMethodHandles) {
+ verify_AdapterMethodHandle(mh, argnum, CHECK);
+ }
+
const char* err = NULL;
+ if (!conv_op_supported(conv_op)) {
+ err = "adapter not yet implemented in the JVM";
+ }
+
// Now it's time to finish the case analysis and pick a MethodHandleEntry.
switch (ek_orig) {
case _adapter_retype_only:
@@ -2073,20 +2248,20 @@
} else if (src == T_DOUBLE && dest == T_FLOAT) {
ek_opt = _adapter_opt_d2f;
} else {
- assert(false, "");
+ goto throw_not_impl; // runs user code, hence could block
}
break;
case 1 *4+ 2:
- if (src == T_INT && dest == T_LONG) {
+ if ((src == T_INT || is_subword_type(src)) && dest == T_LONG) {
ek_opt = _adapter_opt_i2l;
} else if (src == T_FLOAT && dest == T_DOUBLE) {
ek_opt = _adapter_opt_f2d;
} else {
- assert(false, "");
+ goto throw_not_impl; // runs user code, hence could block
}
break;
default:
- assert(false, "");
+ goto throw_not_impl; // runs user code, hence could block
break;
}
}
@@ -2103,14 +2278,54 @@
ek_opt = _adapter_opt_unboxl;
break;
default:
- assert(false, "");
+ goto throw_not_impl;
break;
}
}
break;
case _adapter_prim_to_ref:
- goto throw_not_impl; // allocates, hence could block
+ {
+ assert(UseRicochetFrames, "else don't come here");
+ // vminfo will be the location to insert the return value
+ vminfo = argslot;
+ ek_opt = _adapter_opt_collect_ref;
+ ensure_vmlayout_field(target, CHECK);
+ // for MethodHandleWalk:
+ if (java_lang_invoke_AdapterMethodHandle::is_instance(argument()))
+ ensure_vmlayout_field(argument, CHECK);
+ if (!OptimizeMethodHandles) break;
+ switch (type2size[src]) {
+ case 1:
+ ek_try = EntryKind(_adapter_opt_filter_S0_ref + argslot);
+ if (ek_try < _adapter_opt_collect_LAST &&
+ ek_adapter_opt_collect_slot(ek_try) == argslot) {
+ assert(ek_adapter_opt_collect_count(ek_try) == 1 &&
+ ek_adapter_opt_collect_type(ek_try) == T_OBJECT, "");
+ ek_opt = ek_try;
+ break;
+ }
+ // else downgrade to variable slot:
+ ek_opt = _adapter_opt_collect_1_ref;
+ break;
+ case 2:
+ ek_try = EntryKind(_adapter_opt_collect_2_S0_ref + argslot);
+ if (ek_try < _adapter_opt_collect_LAST &&
+ ek_adapter_opt_collect_slot(ek_try) == argslot) {
+ assert(ek_adapter_opt_collect_count(ek_try) == 2 &&
+ ek_adapter_opt_collect_type(ek_try) == T_OBJECT, "");
+ ek_opt = ek_try;
+ break;
+ }
+ // else downgrade to variable slot:
+ ek_opt = _adapter_opt_collect_2_ref;
+ break;
+ default:
+ goto throw_not_impl;
+ break;
+ }
+ }
+ break;
case _adapter_swap_args:
case _adapter_rot_args:
@@ -2130,35 +2345,184 @@
rotate > 0 ? _adapter_opt_rot_2_up : _adapter_opt_rot_2_down);
break;
default:
- assert(false, "");
+ goto throw_not_impl;
break;
}
}
break;
- case _adapter_collect_args:
- goto throw_not_impl; // allocates, hence could block
-
case _adapter_spread_args:
{
+#ifdef TARGET_ARCH_NYI_6939861
+ // ports before 6939861 supported only three kinds of spread ops
+ if (!UseRicochetFrames) {
+ 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;
+ }
+ break;
+ }
+#endif //TARGET_ARCH_NYI_6939861
// 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, "");
+ int array_size = (slots_pushed + 1) / (type2size[dest] == 2 ? 2 : 1);
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;
+ // general case
+ switch (dest) {
+ case T_BOOLEAN : // fall through to T_BYTE:
+ case T_BYTE : ek_opt = _adapter_opt_spread_byte; break;
+ case T_CHAR : ek_opt = _adapter_opt_spread_char; break;
+ case T_SHORT : ek_opt = _adapter_opt_spread_short; break;
+ case T_INT : ek_opt = _adapter_opt_spread_int; break;
+ case T_LONG : ek_opt = _adapter_opt_spread_long; break;
+ case T_FLOAT : ek_opt = _adapter_opt_spread_float; break;
+ case T_DOUBLE : ek_opt = _adapter_opt_spread_double; break;
+ case T_OBJECT : ek_opt = _adapter_opt_spread_ref; break;
+ case T_VOID : if (array_size != 0) goto throw_not_impl;
+ ek_opt = _adapter_opt_spread_ref; break;
+ default : goto throw_not_impl;
}
- if ((vminfo & CONV_VMINFO_MASK) != vminfo)
- goto throw_not_impl; // overflow
+ assert(array_size == 0 || // it doesn't matter what the spreader is
+ (ek_adapter_opt_spread_count(ek_opt) == -1 &&
+ (ek_adapter_opt_spread_type(ek_opt) == dest ||
+ (ek_adapter_opt_spread_type(ek_opt) == T_BYTE && dest == T_BOOLEAN))),
+ err_msg("dest=%d ek_opt=%d", dest, ek_opt));
+
+ if (array_size <= 0) {
+ // since the general case does not handle length 0, this case is required:
+ ek_opt = _adapter_opt_spread_0;
+ break;
+ }
+ if (dest == T_OBJECT) {
+ ek_try = EntryKind(_adapter_opt_spread_1_ref - 1 + array_size);
+ if (ek_try < _adapter_opt_spread_LAST &&
+ ek_adapter_opt_spread_count(ek_try) == array_size) {
+ assert(ek_adapter_opt_spread_type(ek_try) == dest, "");
+ ek_opt = ek_try;
+ break;
+ }
+ }
+ break;
}
break;
- case _adapter_flyby:
- case _adapter_ricochet:
- goto throw_not_impl; // runs Java code, hence could block
+ case _adapter_collect_args:
+ {
+ assert(UseRicochetFrames, "else don't come here");
+ int elem_slots = argument_slot_count(java_lang_invoke_MethodHandle::type(argument()));
+ // vminfo will be the location to insert the return value
+ vminfo = argslot;
+ ensure_vmlayout_field(target, CHECK);
+ ensure_vmlayout_field(argument, CHECK);
+
+ // general case:
+ switch (dest) {
+ default : if (!is_subword_type(dest)) goto throw_not_impl;
+ // else fall through:
+ case T_INT : ek_opt = _adapter_opt_collect_int; break;
+ case T_LONG : ek_opt = _adapter_opt_collect_long; break;
+ case T_FLOAT : ek_opt = _adapter_opt_collect_float; break;
+ case T_DOUBLE : ek_opt = _adapter_opt_collect_double; break;
+ case T_OBJECT : ek_opt = _adapter_opt_collect_ref; break;
+ case T_VOID : ek_opt = _adapter_opt_collect_void; break;
+ }
+ assert(ek_adapter_opt_collect_slot(ek_opt) == -1 &&
+ ek_adapter_opt_collect_count(ek_opt) == -1 &&
+ (ek_adapter_opt_collect_type(ek_opt) == dest ||
+ ek_adapter_opt_collect_type(ek_opt) == T_INT && is_subword_type(dest)),
+ "");
+
+ if (dest == T_OBJECT && elem_slots == 1 && OptimizeMethodHandles) {
+ // filter operation on a ref
+ ek_try = EntryKind(_adapter_opt_filter_S0_ref + argslot);
+ if (ek_try < _adapter_opt_collect_LAST &&
+ ek_adapter_opt_collect_slot(ek_try) == argslot) {
+ assert(ek_adapter_opt_collect_count(ek_try) == elem_slots &&
+ ek_adapter_opt_collect_type(ek_try) == dest, "");
+ ek_opt = ek_try;
+ break;
+ }
+ ek_opt = _adapter_opt_collect_1_ref;
+ break;
+ }
+
+ if (dest == T_OBJECT && elem_slots == 2 && OptimizeMethodHandles) {
+ // filter of two arguments
+ ek_try = EntryKind(_adapter_opt_collect_2_S0_ref + argslot);
+ if (ek_try < _adapter_opt_collect_LAST &&
+ ek_adapter_opt_collect_slot(ek_try) == argslot) {
+ assert(ek_adapter_opt_collect_count(ek_try) == elem_slots &&
+ ek_adapter_opt_collect_type(ek_try) == dest, "");
+ ek_opt = ek_try;
+ break;
+ }
+ ek_opt = _adapter_opt_collect_2_ref;
+ break;
+ }
+
+ if (dest == T_OBJECT && OptimizeMethodHandles) {
+ // try to use a fixed length adapter
+ ek_try = EntryKind(_adapter_opt_collect_0_ref + elem_slots);
+ if (ek_try < _adapter_opt_collect_LAST &&
+ ek_adapter_opt_collect_count(ek_try) == elem_slots) {
+ assert(ek_adapter_opt_collect_slot(ek_try) == -1 &&
+ ek_adapter_opt_collect_type(ek_try) == dest, "");
+ ek_opt = ek_try;
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case _adapter_fold_args:
+ {
+ assert(UseRicochetFrames, "else don't come here");
+ int elem_slots = argument_slot_count(java_lang_invoke_MethodHandle::type(argument()));
+ // vminfo will be the location to insert the return value
+ vminfo = argslot + elem_slots;
+ ensure_vmlayout_field(target, CHECK);
+ ensure_vmlayout_field(argument, CHECK);
+
+ switch (dest) {
+ default : if (!is_subword_type(dest)) goto throw_not_impl;
+ // else fall through:
+ case T_INT : ek_opt = _adapter_opt_fold_int; break;
+ case T_LONG : ek_opt = _adapter_opt_fold_long; break;
+ case T_FLOAT : ek_opt = _adapter_opt_fold_float; break;
+ case T_DOUBLE : ek_opt = _adapter_opt_fold_double; break;
+ case T_OBJECT : ek_opt = _adapter_opt_fold_ref; break;
+ case T_VOID : ek_opt = _adapter_opt_fold_void; break;
+ }
+ assert(ek_adapter_opt_collect_slot(ek_opt) == -1 &&
+ ek_adapter_opt_collect_count(ek_opt) == -1 &&
+ (ek_adapter_opt_collect_type(ek_opt) == dest ||
+ ek_adapter_opt_collect_type(ek_opt) == T_INT && is_subword_type(dest)),
+ "");
+
+ if (dest == T_OBJECT && elem_slots == 0 && OptimizeMethodHandles) {
+ // if there are no args, just pretend it's a collect
+ ek_opt = _adapter_opt_collect_0_ref;
+ break;
+ }
+
+ if (dest == T_OBJECT && OptimizeMethodHandles) {
+ // try to use a fixed length adapter
+ ek_try = EntryKind(_adapter_opt_fold_1_ref - 1 + elem_slots);
+ if (ek_try < _adapter_opt_fold_LAST &&
+ ek_adapter_opt_collect_count(ek_try) == elem_slots) {
+ assert(ek_adapter_opt_collect_slot(ek_try) == -1 &&
+ ek_adapter_opt_collect_type(ek_try) == dest, "");
+ ek_opt = ek_try;
+ break;
+ }
+ }
+
+ break;
+ }
default:
// should have failed much earlier; must be a missing case here
@@ -2166,11 +2530,36 @@
// and fall through:
throw_not_impl:
- // FIXME: these adapters are NYI
- err = "adapter not yet implemented in the JVM";
+ if (err == NULL)
+ err = "unknown adapter type";
break;
}
+ if (err == NULL && (vminfo & CONV_VMINFO_MASK) != vminfo) {
+ // should not happen, since vminfo is used to encode arg/slot indexes < 255
+ err = "vminfo overflow";
+ }
+
+ if (err == NULL && !have_entry(ek_opt)) {
+ err = "adapter stub for this kind of method handle is missing";
+ }
+
+ if (err == NULL && ek_opt == ek_orig) {
+ switch (ek_opt) {
+ case _adapter_prim_to_prim:
+ case _adapter_ref_to_prim:
+ case _adapter_prim_to_ref:
+ case _adapter_swap_args:
+ case _adapter_rot_args:
+ case _adapter_collect_args:
+ case _adapter_fold_args:
+ case _adapter_spread_args:
+ // should be handled completely by optimized cases; see above
+ err = "init_AdapterMethodHandle should not issue this";
+ break;
+ }
+ }
+
if (err != NULL) {
throw_InternalError_for_bad_conversion(conversion, err, THREAD);
return;
@@ -2190,6 +2579,26 @@
// Java code can publish it in global data structures.
}
+void MethodHandles::ensure_vmlayout_field(Handle target, TRAPS) {
+ Handle mtype(THREAD, java_lang_invoke_MethodHandle::type(target()));
+ Handle mtform(THREAD, java_lang_invoke_MethodType::form(mtype()));
+ if (mtform.is_null()) { THROW(vmSymbols::java_lang_InternalError()); }
+ if (java_lang_invoke_MethodTypeForm::vmlayout_offset_in_bytes() > 0) {
+ if (java_lang_invoke_MethodTypeForm::vmlayout(mtform()) == NULL) {
+ // fill it in
+ Handle erased_mtype(THREAD, java_lang_invoke_MethodTypeForm::erasedType(mtform()));
+ TempNewSymbol erased_signature
+ = java_lang_invoke_MethodType::as_signature(erased_mtype(), /*intern:*/true, CHECK);
+ methodOop cookie
+ = SystemDictionary::find_method_handle_invoke(vmSymbols::invokeExact_name(),
+ erased_signature,
+ SystemDictionaryHandles::Object_klass(),
+ THREAD);
+ java_lang_invoke_MethodTypeForm::init_vmlayout(mtform(), cookie);
+ }
+ }
+}
+
//
// Here are the native methods on sun.invoke.MethodHandleImpl.
// They are the private interface between this JVM and the HotSpot-specific
@@ -2360,8 +2769,10 @@
#ifndef PRODUCT
#define EACH_NAMED_CON(template) \
- template(MethodHandles,GC_JVM_PUSH_LIMIT) \
- template(MethodHandles,GC_JVM_STACK_MOVE_UNIT) \
+ /* hold back this one until JDK stabilizes */ \
+ /* template(MethodHandles,GC_JVM_PUSH_LIMIT) */ \
+ /* hold back this one until JDK stabilizes */ \
+ /* template(MethodHandles,GC_JVM_STACK_MOVE_UNIT) */ \
template(MethodHandles,ETF_HANDLE_OR_METHOD_NAME) \
template(MethodHandles,ETF_DIRECT_HANDLE) \
template(MethodHandles,ETF_METHOD_NAME) \
@@ -2385,9 +2796,8 @@
template(java_lang_invoke_AdapterMethodHandle,OP_DROP_ARGS) \
template(java_lang_invoke_AdapterMethodHandle,OP_COLLECT_ARGS) \
template(java_lang_invoke_AdapterMethodHandle,OP_SPREAD_ARGS) \
- template(java_lang_invoke_AdapterMethodHandle,OP_FLYBY) \
- template(java_lang_invoke_AdapterMethodHandle,OP_RICOCHET) \
- template(java_lang_invoke_AdapterMethodHandle,CONV_OP_LIMIT) \
+ /* hold back this one until JDK stabilizes */ \
+ /*template(java_lang_invoke_AdapterMethodHandle,CONV_OP_LIMIT)*/ \
template(java_lang_invoke_AdapterMethodHandle,CONV_OP_MASK) \
template(java_lang_invoke_AdapterMethodHandle,CONV_VMINFO_MASK) \
template(java_lang_invoke_AdapterMethodHandle,CONV_VMINFO_SHIFT) \
--- a/hotspot/src/share/vm/prims/methodHandles.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/prims/methodHandles.hpp Tue May 24 14:32:10 2011 -0700
@@ -66,8 +66,8 @@
_adapter_drop_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_DROP_ARGS,
_adapter_collect_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS,
_adapter_spread_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS,
- _adapter_flyby = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_FLYBY,
- _adapter_ricochet = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_RICOCHET,
+ _adapter_fold_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS,
+ _adapter_unused_13 = _adapter_mh_first + 13, //hole in the CONV_OP enumeration
_adapter_mh_last = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::CONV_OP_LIMIT - 1,
// Optimized adapter types
@@ -93,10 +93,99 @@
_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,
+ // %% Maybe tame the following with a VM_SYMBOLS_DO type macro?
+
+ // how a blocking adapter returns (platform-dependent)
+ _adapter_opt_return_ref,
+ _adapter_opt_return_int,
+ _adapter_opt_return_long,
+ _adapter_opt_return_float,
+ _adapter_opt_return_double,
+ _adapter_opt_return_void,
+ _adapter_opt_return_S0_ref, // return ref to S=0 (last slot)
+ _adapter_opt_return_S1_ref, // return ref to S=1 (2nd-to-last slot)
+ _adapter_opt_return_S2_ref,
+ _adapter_opt_return_S3_ref,
+ _adapter_opt_return_S4_ref,
+ _adapter_opt_return_S5_ref,
+ _adapter_opt_return_any, // dynamically select r/i/l/f/d
+ _adapter_opt_return_FIRST = _adapter_opt_return_ref,
+ _adapter_opt_return_LAST = _adapter_opt_return_any,
+
+ // spreading (array length cases 0, 1, ...)
+ _adapter_opt_spread_0, // spread empty array to N=0 arguments
+ _adapter_opt_spread_1_ref, // spread Object[] to N=1 argument
+ _adapter_opt_spread_2_ref, // spread Object[] to N=2 arguments
+ _adapter_opt_spread_3_ref, // spread Object[] to N=3 arguments
+ _adapter_opt_spread_4_ref, // spread Object[] to N=4 arguments
+ _adapter_opt_spread_5_ref, // spread Object[] to N=5 arguments
+ _adapter_opt_spread_ref, // spread Object[] to N arguments
+ _adapter_opt_spread_byte, // spread byte[] or boolean[] to N arguments
+ _adapter_opt_spread_char, // spread char[], etc., to N arguments
+ _adapter_opt_spread_short, // spread short[], etc., to N arguments
+ _adapter_opt_spread_int, // spread int[], short[], etc., to N arguments
+ _adapter_opt_spread_long, // spread long[] to N arguments
+ _adapter_opt_spread_float, // spread float[] to N arguments
+ _adapter_opt_spread_double, // spread double[] to N arguments
+ _adapter_opt_spread_FIRST = _adapter_opt_spread_0,
+ _adapter_opt_spread_LAST = _adapter_opt_spread_double,
+
+ // blocking filter/collect conversions
+ // These collect N arguments and replace them (at slot S) by a return value
+ // which is passed to the final target, along with the unaffected arguments.
+ // collect_{N}_{T} collects N arguments at any position into a T value
+ // collect_{N}_S{S}_{T} collects N arguments at slot S into a T value
+ // collect_{T} collects any number of arguments at any position
+ // filter_S{S}_{T} is the same as collect_1_S{S}_{T} (a unary collection)
+ // (collect_2 is also usable as a filter, with long or double arguments)
+ _adapter_opt_collect_ref, // combine N arguments, replace with a reference
+ _adapter_opt_collect_int, // combine N arguments, replace with an int, short, etc.
+ _adapter_opt_collect_long, // combine N arguments, replace with a long
+ _adapter_opt_collect_float, // combine N arguments, replace with a float
+ _adapter_opt_collect_double, // combine N arguments, replace with a double
+ _adapter_opt_collect_void, // combine N arguments, replace with nothing
+ // if there is a small fixed number to push, do so without a loop:
+ _adapter_opt_collect_0_ref, // collect N=0 arguments, insert a reference
+ _adapter_opt_collect_1_ref, // collect N=1 argument, replace with a reference
+ _adapter_opt_collect_2_ref, // combine N=2 arguments, replace with a reference
+ _adapter_opt_collect_3_ref, // combine N=3 arguments, replace with a reference
+ _adapter_opt_collect_4_ref, // combine N=4 arguments, replace with a reference
+ _adapter_opt_collect_5_ref, // combine N=5 arguments, replace with a reference
+ // filters are an important special case because they never move arguments:
+ _adapter_opt_filter_S0_ref, // filter N=1 argument at S=0, replace with a reference
+ _adapter_opt_filter_S1_ref, // filter N=1 argument at S=1, replace with a reference
+ _adapter_opt_filter_S2_ref, // filter N=1 argument at S=2, replace with a reference
+ _adapter_opt_filter_S3_ref, // filter N=1 argument at S=3, replace with a reference
+ _adapter_opt_filter_S4_ref, // filter N=1 argument at S=4, replace with a reference
+ _adapter_opt_filter_S5_ref, // filter N=1 argument at S=5, replace with a reference
+ // these move arguments, but they are important for boxing
+ _adapter_opt_collect_2_S0_ref, // combine last N=2 arguments, replace with a reference
+ _adapter_opt_collect_2_S1_ref, // combine N=2 arguments at S=1, replace with a reference
+ _adapter_opt_collect_2_S2_ref, // combine N=2 arguments at S=2, replace with a reference
+ _adapter_opt_collect_2_S3_ref, // combine N=2 arguments at S=3, replace with a reference
+ _adapter_opt_collect_2_S4_ref, // combine N=2 arguments at S=4, replace with a reference
+ _adapter_opt_collect_2_S5_ref, // combine N=2 arguments at S=5, replace with a reference
+ _adapter_opt_collect_FIRST = _adapter_opt_collect_ref,
+ _adapter_opt_collect_LAST = _adapter_opt_collect_2_S5_ref,
+
+ // blocking folding conversions
+ // these are like collects, but retain all the N arguments for the final target
+ //_adapter_opt_fold_0_ref, // same as _adapter_opt_collect_0_ref
+ // fold_{N}_{T} processes N arguments at any position into a T value, which it inserts
+ // fold_{T} processes any number of arguments at any position
+ _adapter_opt_fold_ref, // process N arguments, prepend a reference
+ _adapter_opt_fold_int, // process N arguments, prepend an int, short, etc.
+ _adapter_opt_fold_long, // process N arguments, prepend a long
+ _adapter_opt_fold_float, // process N arguments, prepend a float
+ _adapter_opt_fold_double, // process N arguments, prepend a double
+ _adapter_opt_fold_void, // process N arguments, but leave the list unchanged
+ _adapter_opt_fold_1_ref, // process N=1 argument, prepend a reference
+ _adapter_opt_fold_2_ref, // process N=2 arguments, prepend a reference
+ _adapter_opt_fold_3_ref, // process N=3 arguments, prepend a reference
+ _adapter_opt_fold_4_ref, // process N=4 arguments, prepend a reference
+ _adapter_opt_fold_5_ref, // process N=5 arguments, prepend a reference
+ _adapter_opt_fold_FIRST = _adapter_opt_fold_ref,
+ _adapter_opt_fold_LAST = _adapter_opt_fold_5_ref,
_EK_LIMIT,
_EK_FIRST = 0
@@ -110,6 +199,7 @@
enum { // import java_lang_invoke_AdapterMethodHandle::CONV_OP_*
CONV_OP_LIMIT = java_lang_invoke_AdapterMethodHandle::CONV_OP_LIMIT,
CONV_OP_MASK = java_lang_invoke_AdapterMethodHandle::CONV_OP_MASK,
+ CONV_TYPE_MASK = java_lang_invoke_AdapterMethodHandle::CONV_TYPE_MASK,
CONV_VMINFO_MASK = java_lang_invoke_AdapterMethodHandle::CONV_VMINFO_MASK,
CONV_VMINFO_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_VMINFO_SHIFT,
CONV_OP_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_OP_SHIFT,
@@ -123,6 +213,7 @@
static MethodHandleEntry* _entries[_EK_LIMIT];
static const char* _entry_names[_EK_LIMIT+1];
static jobject _raise_exception_method;
+ static address _adapter_return_handlers[CONV_TYPE_MASK+1];
// Adapters.
static MethodHandlesAdapterBlob* _adapter_code;
@@ -147,39 +238,195 @@
}
// Some adapter helper functions.
- static void get_ek_bound_mh_info(EntryKind ek, BasicType& arg_type, int& arg_mask, int& arg_slots) {
+ static EntryKind ek_original_kind(EntryKind ek) {
+ if (ek <= _adapter_mh_last) return ek;
switch (ek) {
- case _bound_int_mh : // fall-thru
- case _bound_int_direct_mh : arg_type = T_INT; arg_mask = _INSERT_INT_MASK; break;
- case _bound_long_mh : // fall-thru
- case _bound_long_direct_mh: arg_type = T_LONG; arg_mask = _INSERT_LONG_MASK; break;
- case _bound_ref_mh : // fall-thru
- case _bound_ref_direct_mh : arg_type = T_OBJECT; arg_mask = _INSERT_REF_MASK; break;
- default: ShouldNotReachHere();
+ case _adapter_opt_swap_1:
+ case _adapter_opt_swap_2:
+ return _adapter_swap_args;
+ case _adapter_opt_rot_1_up:
+ case _adapter_opt_rot_1_down:
+ case _adapter_opt_rot_2_up:
+ case _adapter_opt_rot_2_down:
+ return _adapter_rot_args;
+ case _adapter_opt_i2i:
+ case _adapter_opt_l2i:
+ case _adapter_opt_d2f:
+ case _adapter_opt_i2l:
+ case _adapter_opt_f2d:
+ return _adapter_prim_to_prim;
+ case _adapter_opt_unboxi:
+ case _adapter_opt_unboxl:
+ return _adapter_ref_to_prim;
}
- arg_slots = type2size[arg_type];
+ if (ek >= _adapter_opt_spread_FIRST && ek <= _adapter_opt_spread_LAST)
+ return _adapter_spread_args;
+ if (ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST)
+ return _adapter_collect_args;
+ if (ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST)
+ return _adapter_fold_args;
+ if (ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST)
+ return _adapter_opt_return_any;
+ assert(false, "oob");
+ return _EK_LIMIT;
+ }
+
+ static bool ek_supported(MethodHandles::EntryKind ek);
+
+ static BasicType ek_bound_mh_arg_type(EntryKind ek) {
+ switch (ek) {
+ case _bound_int_mh : // fall-thru
+ case _bound_int_direct_mh : return T_INT;
+ case _bound_long_mh : // fall-thru
+ case _bound_long_direct_mh : return T_LONG;
+ default : return T_OBJECT;
+ }
+ }
+
+ static int ek_adapter_opt_swap_slots(EntryKind ek) {
+ switch (ek) {
+ case _adapter_opt_swap_1 : return 1;
+ case _adapter_opt_swap_2 : return 2;
+ case _adapter_opt_rot_1_up : return 1;
+ case _adapter_opt_rot_1_down : return 1;
+ case _adapter_opt_rot_2_up : return 2;
+ case _adapter_opt_rot_2_down : return 2;
+ default : ShouldNotReachHere(); return -1;
+ }
+ }
+
+ static int ek_adapter_opt_swap_mode(EntryKind ek) {
+ switch (ek) {
+ case _adapter_opt_swap_1 : return 0;
+ case _adapter_opt_swap_2 : return 0;
+ case _adapter_opt_rot_1_up : return 1;
+ case _adapter_opt_rot_1_down : return -1;
+ case _adapter_opt_rot_2_up : return 1;
+ case _adapter_opt_rot_2_down : return -1;
+ default : ShouldNotReachHere(); return 0;
+ }
}
- static void get_ek_adapter_opt_swap_rot_info(EntryKind ek, int& swap_bytes, int& rotate) {
- int swap_slots = 0;
+ static int ek_adapter_opt_collect_count(EntryKind ek) {
+ assert(ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST ||
+ ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST, "");
switch (ek) {
- case _adapter_opt_swap_1: swap_slots = 1; rotate = 0; break;
- case _adapter_opt_swap_2: swap_slots = 2; rotate = 0; break;
- case _adapter_opt_rot_1_up: swap_slots = 1; rotate = 1; break;
- case _adapter_opt_rot_1_down: swap_slots = 1; rotate = -1; break;
- case _adapter_opt_rot_2_up: swap_slots = 2; rotate = 1; break;
- case _adapter_opt_rot_2_down: swap_slots = 2; rotate = -1; break;
- default: ShouldNotReachHere();
+ case _adapter_opt_collect_0_ref : return 0;
+ case _adapter_opt_filter_S0_ref :
+ case _adapter_opt_filter_S1_ref :
+ case _adapter_opt_filter_S2_ref :
+ case _adapter_opt_filter_S3_ref :
+ case _adapter_opt_filter_S4_ref :
+ case _adapter_opt_filter_S5_ref :
+ case _adapter_opt_fold_1_ref :
+ case _adapter_opt_collect_1_ref : return 1;
+ case _adapter_opt_collect_2_S0_ref :
+ case _adapter_opt_collect_2_S1_ref :
+ case _adapter_opt_collect_2_S2_ref :
+ case _adapter_opt_collect_2_S3_ref :
+ case _adapter_opt_collect_2_S4_ref :
+ case _adapter_opt_collect_2_S5_ref :
+ case _adapter_opt_fold_2_ref :
+ case _adapter_opt_collect_2_ref : return 2;
+ case _adapter_opt_fold_3_ref :
+ case _adapter_opt_collect_3_ref : return 3;
+ case _adapter_opt_fold_4_ref :
+ case _adapter_opt_collect_4_ref : return 4;
+ case _adapter_opt_fold_5_ref :
+ case _adapter_opt_collect_5_ref : return 5;
+ default : return -1; // sentinel value for "variable"
}
- // Return the size of the stack slots to move in bytes.
- swap_bytes = swap_slots * Interpreter::stackElementSize;
+ }
+
+ static int ek_adapter_opt_collect_slot(EntryKind ek) {
+ assert(ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST ||
+ ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST, "");
+ switch (ek) {
+ case _adapter_opt_collect_2_S0_ref :
+ case _adapter_opt_filter_S0_ref : return 0;
+ case _adapter_opt_collect_2_S1_ref :
+ case _adapter_opt_filter_S1_ref : return 1;
+ case _adapter_opt_collect_2_S2_ref :
+ case _adapter_opt_filter_S2_ref : return 2;
+ case _adapter_opt_collect_2_S3_ref :
+ case _adapter_opt_filter_S3_ref : return 3;
+ case _adapter_opt_collect_2_S4_ref :
+ case _adapter_opt_filter_S4_ref : return 4;
+ case _adapter_opt_collect_2_S5_ref :
+ case _adapter_opt_filter_S5_ref : return 5;
+ default : return -1; // sentinel value for "variable"
+ }
}
- static int get_ek_adapter_opt_spread_info(EntryKind ek) {
+ static BasicType ek_adapter_opt_collect_type(EntryKind ek) {
+ assert(ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST ||
+ ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST, "");
+ switch (ek) {
+ case _adapter_opt_fold_int :
+ case _adapter_opt_collect_int : return T_INT;
+ case _adapter_opt_fold_long :
+ case _adapter_opt_collect_long : return T_LONG;
+ case _adapter_opt_fold_float :
+ case _adapter_opt_collect_float : return T_FLOAT;
+ case _adapter_opt_fold_double :
+ case _adapter_opt_collect_double : return T_DOUBLE;
+ case _adapter_opt_fold_void :
+ case _adapter_opt_collect_void : return T_VOID;
+ default : return T_OBJECT;
+ }
+ }
+
+ static int ek_adapter_opt_return_slot(EntryKind ek) {
+ assert(ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST, "");
+ switch (ek) {
+ case _adapter_opt_return_S0_ref : return 0;
+ case _adapter_opt_return_S1_ref : return 1;
+ case _adapter_opt_return_S2_ref : return 2;
+ case _adapter_opt_return_S3_ref : return 3;
+ case _adapter_opt_return_S4_ref : return 4;
+ case _adapter_opt_return_S5_ref : return 5;
+ default : return -1; // sentinel value for "variable"
+ }
+ }
+
+ static BasicType ek_adapter_opt_return_type(EntryKind ek) {
+ assert(ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST, "");
switch (ek) {
- case _adapter_opt_spread_0: return 0;
- case _adapter_opt_spread_1: return 1;
- default : return -1;
+ case _adapter_opt_return_int : return T_INT;
+ case _adapter_opt_return_long : return T_LONG;
+ case _adapter_opt_return_float : return T_FLOAT;
+ case _adapter_opt_return_double : return T_DOUBLE;
+ case _adapter_opt_return_void : return T_VOID;
+ case _adapter_opt_return_any : return T_CONFLICT; // sentinel value for "variable"
+ default : return T_OBJECT;
+ }
+ }
+
+ static int ek_adapter_opt_spread_count(EntryKind ek) {
+ assert(ek >= _adapter_opt_spread_FIRST && ek <= _adapter_opt_spread_LAST, "");
+ switch (ek) {
+ case _adapter_opt_spread_0 : return 0;
+ case _adapter_opt_spread_1_ref : return 1;
+ case _adapter_opt_spread_2_ref : return 2;
+ case _adapter_opt_spread_3_ref : return 3;
+ case _adapter_opt_spread_4_ref : return 4;
+ case _adapter_opt_spread_5_ref : return 5;
+ default : return -1; // sentinel value for "variable"
+ }
+ }
+
+ static BasicType ek_adapter_opt_spread_type(EntryKind ek) {
+ assert(ek >= _adapter_opt_spread_FIRST && ek <= _adapter_opt_spread_LAST, "");
+ switch (ek) {
+ // (there is no _adapter_opt_spread_boolean; we use byte)
+ case _adapter_opt_spread_byte : return T_BYTE;
+ case _adapter_opt_spread_char : return T_CHAR;
+ case _adapter_opt_spread_short : return T_SHORT;
+ case _adapter_opt_spread_int : return T_INT;
+ case _adapter_opt_spread_long : return T_LONG;
+ case _adapter_opt_spread_float : return T_FLOAT;
+ case _adapter_opt_spread_double : return T_DOUBLE;
+ default : return T_OBJECT;
}
}
@@ -228,12 +475,21 @@
// Bit mask of conversion_op values. May vary by platform.
static int adapter_conversion_ops_supported_mask();
+ static bool conv_op_supported(int conv_op) {
+ assert(conv_op_valid(conv_op), "");
+ return ((adapter_conversion_ops_supported_mask() & nth_bit(conv_op)) != 0);
+ }
+
// 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;
}
+ // Adapter frame traversal. (Implementation-specific.)
+ static frame ricochet_frame_sender(const frame& fr, RegisterMap* reg_map);
+ static void ricochet_frame_oops_do(const frame& fr, OopClosure* blk, const RegisterMap* reg_map);
+
enum { CONV_VMINFO_SIGN_FLAG = 0x80 };
// Shift values for prim-to-prim conversions.
static int adapter_prim_to_prim_subword_vminfo(BasicType dest) {
@@ -429,6 +685,7 @@
// 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);
+ static void ensure_vmlayout_field(Handle target, TRAPS);
#ifdef ASSERT
static bool spot_check_entry_names();
@@ -441,6 +698,8 @@
KlassHandle receiver_klass,
TRAPS);
+public:
+ static bool is_float_fixed_reinterpretation_cast(BasicType src, BasicType dst);
static bool same_basic_type_for_arguments(BasicType src, BasicType dst,
bool raw = false,
bool for_return = false);
@@ -448,12 +707,54 @@
return same_basic_type_for_arguments(src, dst, raw, true);
}
- enum { // arg_mask values
+ static Symbol* convert_to_signature(oop type_str, bool polymorphic, TRAPS);
+
+#ifdef TARGET_ARCH_x86
+# include "methodHandles_x86.hpp"
+#endif
+#ifdef TARGET_ARCH_sparc
+#define TARGET_ARCH_NYI_6939861 1 //FIXME
+//# include "methodHandles_sparc.hpp"
+#endif
+#ifdef TARGET_ARCH_zero
+#define TARGET_ARCH_NYI_6939861 1 //FIXME
+//# include "methodHandles_zero.hpp"
+#endif
+#ifdef TARGET_ARCH_arm
+#define TARGET_ARCH_NYI_6939861 1 //FIXME
+//# include "methodHandles_arm.hpp"
+#endif
+#ifdef TARGET_ARCH_ppc
+#define TARGET_ARCH_NYI_6939861 1 //FIXME
+//# include "methodHandles_ppc.hpp"
+#endif
+
+#ifdef TARGET_ARCH_NYI_6939861
+ // Here are some backward compatible declarations until the 6939861 ports are updated.
+ #define _adapter_flyby (_EK_LIMIT + 10)
+ #define _adapter_ricochet (_EK_LIMIT + 11)
+ #define _adapter_opt_spread_1 _adapter_opt_spread_1_ref
+ #define _adapter_opt_spread_more _adapter_opt_spread_ref
+ enum {
_INSERT_NO_MASK = -1,
_INSERT_REF_MASK = 0,
_INSERT_INT_MASK = 1,
_INSERT_LONG_MASK = 3
};
+ static void get_ek_bound_mh_info(EntryKind ek, BasicType& arg_type, int& arg_mask, int& arg_slots) {
+ arg_type = ek_bound_mh_arg_type(ek);
+ arg_mask = 0;
+ arg_slots = type2size[arg_type];;
+ }
+ static void get_ek_adapter_opt_swap_rot_info(EntryKind ek, int& swap_bytes, int& rotate) {
+ int swap_slots = ek_adapter_opt_swap_slots(ek);
+ rotate = ek_adapter_opt_swap_mode(ek);
+ swap_bytes = swap_slots * Interpreter::stackElementSize;
+ }
+ static int get_ek_adapter_opt_spread_info(EntryKind ek) {
+ return ek_adapter_opt_spread_count(ek);
+ }
+
static void insert_arg_slots(MacroAssembler* _masm,
RegisterOrConstant arg_slots,
int arg_mask,
@@ -466,8 +767,7 @@
Register temp_reg, Register temp2_reg, Register temp3_reg = noreg);
static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
-
- static Symbol* convert_to_signature(oop type_str, bool polymorphic, TRAPS);
+#endif //TARGET_ARCH_NYI_6939861
};
--- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Tue May 24 14:32:10 2011 -0700
@@ -1,7 +1,26 @@
/*
-* Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved.
-* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
-*/
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
#include "precompiled.hpp"
#include "runtime/advancedThresholdPolicy.hpp"
--- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp Tue May 24 14:32:10 2011 -0700
@@ -1,7 +1,26 @@
/*
-* Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved.
-* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
-*/
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
#ifndef SHARE_VM_RUNTIME_ADVANCEDTHRESHOLDPOLICY_HPP
#define SHARE_VM_RUNTIME_ADVANCEDTHRESHOLDPOLICY_HPP
--- a/hotspot/src/share/vm/runtime/deoptimization.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/deoptimization.cpp Tue May 24 14:32:10 2011 -0700
@@ -90,12 +90,14 @@
Deoptimization::UnrollBlock::UnrollBlock(int size_of_deoptimized_frame,
int caller_adjustment,
+ int caller_actual_parameters,
int number_of_frames,
intptr_t* frame_sizes,
address* frame_pcs,
BasicType return_type) {
_size_of_deoptimized_frame = size_of_deoptimized_frame;
_caller_adjustment = caller_adjustment;
+ _caller_actual_parameters = caller_actual_parameters;
_number_of_frames = number_of_frames;
_frame_sizes = frame_sizes;
_frame_pcs = frame_pcs;
@@ -373,6 +375,28 @@
popframe_extra_args = in_words(thread->popframe_preserved_args_size_in_words());
}
+ // Find the current pc for sender of the deoptee. Since the sender may have been deoptimized
+ // itself since the deoptee vframeArray was created we must get a fresh value of the pc rather
+ // than simply use array->sender.pc(). This requires us to walk the current set of frames
+ //
+ frame deopt_sender = stub_frame.sender(&dummy_map); // First is the deoptee frame
+ deopt_sender = deopt_sender.sender(&dummy_map); // Now deoptee caller
+
+ // It's possible that the number of paramters at the call site is
+ // different than number of arguments in the callee when method
+ // handles are used. If the caller is interpreted get the real
+ // value so that the proper amount of space can be added to it's
+ // frame.
+ int caller_actual_parameters = callee_parameters;
+ if (deopt_sender.is_interpreted_frame()) {
+ methodHandle method = deopt_sender.interpreter_frame_method();
+ Bytecode_invoke cur = Bytecode_invoke_check(method,
+ deopt_sender.interpreter_frame_bci());
+ Symbol* signature = method->constants()->signature_ref_at(cur.index());
+ ArgumentSizeComputer asc(signature);
+ caller_actual_parameters = asc.size() + (cur.has_receiver() ? 1 : 0);
+ }
+
//
// frame_sizes/frame_pcs[0] oldest frame (int or c2i)
// frame_sizes/frame_pcs[1] next oldest frame (int)
@@ -391,7 +415,13 @@
// frame[number_of_frames - 1 ] = on_stack_size(youngest)
// frame[number_of_frames - 2 ] = on_stack_size(sender(youngest))
// frame[number_of_frames - 3 ] = on_stack_size(sender(sender(youngest)))
- frame_sizes[number_of_frames - 1 - index] = BytesPerWord * array->element(index)->on_stack_size(callee_parameters,
+ int caller_parms = callee_parameters;
+ if (index == array->frames() - 1) {
+ // Use the value from the interpreted caller
+ caller_parms = caller_actual_parameters;
+ }
+ frame_sizes[number_of_frames - 1 - index] = BytesPerWord * array->element(index)->on_stack_size(caller_parms,
+ callee_parameters,
callee_locals,
index == 0,
popframe_extra_args);
@@ -418,28 +448,6 @@
// Compute information for handling adapters and adjusting the frame size of the caller.
int caller_adjustment = 0;
- // Find the current pc for sender of the deoptee. Since the sender may have been deoptimized
- // itself since the deoptee vframeArray was created we must get a fresh value of the pc rather
- // than simply use array->sender.pc(). This requires us to walk the current set of frames
- //
- frame deopt_sender = stub_frame.sender(&dummy_map); // First is the deoptee frame
- deopt_sender = deopt_sender.sender(&dummy_map); // Now deoptee caller
-
- // It's possible that the number of paramters at the call site is
- // different than number of arguments in the callee when method
- // handles are used. If the caller is interpreted get the real
- // value so that the proper amount of space can be added to it's
- // frame.
- int sender_callee_parameters = callee_parameters;
- if (deopt_sender.is_interpreted_frame()) {
- methodHandle method = deopt_sender.interpreter_frame_method();
- Bytecode_invoke cur = Bytecode_invoke_check(method,
- deopt_sender.interpreter_frame_bci());
- Symbol* signature = method->constants()->signature_ref_at(cur.index());
- ArgumentSizeComputer asc(signature);
- sender_callee_parameters = asc.size() + (cur.has_receiver() ? 1 : 0);
- }
-
// Compute the amount the oldest interpreter frame will have to adjust
// its caller's stack by. If the caller is a compiled frame then
// we pretend that the callee has no parameters so that the
@@ -454,11 +462,11 @@
if (deopt_sender.is_compiled_frame()) {
caller_adjustment = last_frame_adjust(0, callee_locals);
- } else if (callee_locals > sender_callee_parameters) {
+ } else if (callee_locals > caller_actual_parameters) {
// The caller frame may need extending to accommodate
// non-parameter locals of the first unpacked interpreted frame.
// Compute that adjustment.
- caller_adjustment = last_frame_adjust(sender_callee_parameters, callee_locals);
+ caller_adjustment = last_frame_adjust(caller_actual_parameters, callee_locals);
}
// If the sender is deoptimized the we must retrieve the address of the handler
@@ -473,6 +481,7 @@
UnrollBlock* info = new UnrollBlock(array->frame_size() * BytesPerWord,
caller_adjustment * BytesPerWord,
+ caller_actual_parameters,
number_of_frames,
frame_sizes,
frame_pcs,
@@ -570,7 +579,7 @@
UnrollBlock* info = array->unroll_block();
// Unpack the interpreter frames and any adapter frame (c2 only) we might create.
- array->unpack_to_stack(stub_frame, exec_mode);
+ array->unpack_to_stack(stub_frame, exec_mode, info->caller_actual_parameters());
BasicType bt = info->return_type();
--- a/hotspot/src/share/vm/runtime/deoptimization.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/deoptimization.hpp Tue May 24 14:32:10 2011 -0700
@@ -138,6 +138,9 @@
intptr_t* _register_block; // Block for storing callee-saved registers.
BasicType _return_type; // Tells if we have to restore double or long return value
intptr_t _initial_fp; // FP of the sender frame
+ int _caller_actual_parameters; // The number of actual arguments at the
+ // interpreted caller of the deoptimized frame
+
// The following fields are used as temps during the unpacking phase
// (which is tight on registers, especially on x86). They really ought
// to be PD variables but that involves moving this class into its own
@@ -149,6 +152,7 @@
// Constructor
UnrollBlock(int size_of_deoptimized_frame,
int caller_adjustment,
+ int caller_actual_parameters,
int number_of_frames,
intptr_t* frame_sizes,
address* frames_pcs,
@@ -168,6 +172,8 @@
void set_initial_fp(intptr_t fp) { _initial_fp = fp; }
+ int caller_actual_parameters() const { return _caller_actual_parameters; }
+
// Accessors used by the code generator for the unpack stub.
static int size_of_deoptimized_frame_offset_in_bytes() { return offset_of(UnrollBlock, _size_of_deoptimized_frame); }
static int caller_adjustment_offset_in_bytes() { return offset_of(UnrollBlock, _caller_adjustment); }
--- a/hotspot/src/share/vm/runtime/frame.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/frame.cpp Tue May 24 14:32:10 2011 -0700
@@ -33,6 +33,7 @@
#include "oops/methodOop.hpp"
#include "oops/oop.inline.hpp"
#include "oops/oop.inline2.hpp"
+#include "prims/methodHandles.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp"
@@ -169,6 +170,11 @@
}
// type testers
+bool frame::is_ricochet_frame() const {
+ RicochetBlob* rcb = SharedRuntime::ricochet_blob();
+ return (_cb == rcb && rcb != NULL && rcb->returns_to_bounce_addr(_pc));
+}
+
bool frame::is_deoptimized_frame() const {
assert(_deopt_state != unknown, "not answerable");
return _deopt_state == is_deoptimized;
@@ -341,12 +347,18 @@
frame frame::real_sender(RegisterMap* map) const {
frame result = sender(map);
- while (result.is_runtime_frame()) {
+ while (result.is_runtime_frame() ||
+ result.is_ricochet_frame()) {
result = result.sender(map);
}
return result;
}
+frame frame::sender_for_ricochet_frame(RegisterMap* map) const {
+ assert(is_ricochet_frame(), "");
+ return MethodHandles::ricochet_frame_sender(*this, map);
+}
+
// Note: called by profiler - NOT for current thread
frame frame::profile_find_Java_sender_frame(JavaThread *thread) {
// If we don't recognize this frame, walk back up the stack until we do
@@ -529,6 +541,7 @@
const char* frame::print_name() const {
if (is_native_frame()) return "Native";
if (is_interpreted_frame()) return "Interpreted";
+ if (is_ricochet_frame()) return "Ricochet";
if (is_compiled_frame()) {
if (is_deoptimized_frame()) return "Deoptimized";
return "Compiled";
@@ -715,6 +728,8 @@
st->print("v ~RuntimeStub::%s", ((RuntimeStub *)_cb)->name());
} else if (_cb->is_deoptimization_stub()) {
st->print("v ~DeoptimizationBlob");
+ } else if (_cb->is_ricochet_stub()) {
+ st->print("v ~RichochetBlob");
} else if (_cb->is_exception_stub()) {
st->print("v ~ExceptionBlob");
} else if (_cb->is_safepoint_stub()) {
@@ -978,6 +993,9 @@
void frame::oops_code_blob_do(OopClosure* f, CodeBlobClosure* cf, const RegisterMap* reg_map) {
assert(_cb != NULL, "sanity check");
+ if (_cb == SharedRuntime::ricochet_blob()) {
+ oops_ricochet_do(f, reg_map);
+ }
if (_cb->oop_maps() != NULL) {
OopMapSet::oops_do(this, reg_map, f);
@@ -996,6 +1014,11 @@
cf->do_code_blob(_cb);
}
+void frame::oops_ricochet_do(OopClosure* f, const RegisterMap* map) {
+ assert(is_ricochet_frame(), "");
+ MethodHandles::ricochet_frame_oops_do(*this, f, map);
+}
+
class CompiledArgumentOopFinder: public SignatureInfo {
protected:
OopClosure* _f;
@@ -1400,7 +1423,7 @@
}
-bool FrameValues::validate() {
+void FrameValues::validate() {
_values.sort(compare);
bool error = false;
FrameValue prev;
@@ -1423,19 +1446,32 @@
prev = fv;
}
}
- return error;
+ assert(!error, "invalid layout");
}
void FrameValues::print() {
_values.sort(compare);
- intptr_t* v0 = _values.at(0).location;
- intptr_t* v1 = _values.at(_values.length() - 1).location;
+ JavaThread* thread = JavaThread::current();
+
+ // Sometimes values like the fp can be invalid values if the
+ // register map wasn't updated during the walk. Trim out values
+ // that aren't actually in the stack of the thread.
+ int min_index = 0;
+ int max_index = _values.length() - 1;
+ intptr_t* v0 = _values.at(min_index).location;
+ while (!thread->is_in_stack((address)v0)) {
+ v0 = _values.at(++min_index).location;
+ }
+ intptr_t* v1 = _values.at(max_index).location;
+ while (!thread->is_in_stack((address)v1)) {
+ v1 = _values.at(--max_index).location;
+ }
intptr_t* min = MIN2(v0, v1);
intptr_t* max = MAX2(v0, v1);
intptr_t* cur = max;
intptr_t* last = NULL;
- for (int i = _values.length() - 1; i >= 0; i--) {
+ for (int i = max_index; i >= min_index; i--) {
FrameValue fv = _values.at(i);
while (cur > fv.location) {
tty->print_cr(" " INTPTR_FORMAT ": " INTPTR_FORMAT, cur, *cur);
--- a/hotspot/src/share/vm/runtime/frame.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/frame.hpp Tue May 24 14:32:10 2011 -0700
@@ -135,6 +135,7 @@
bool is_interpreted_frame() const;
bool is_java_frame() const;
bool is_entry_frame() const; // Java frame called from C?
+ bool is_ricochet_frame() const;
bool is_native_frame() const;
bool is_runtime_frame() const;
bool is_compiled_frame() const;
@@ -175,6 +176,7 @@
// Helper methods for better factored code in frame::sender
frame sender_for_compiled_frame(RegisterMap* map) const;
frame sender_for_entry_frame(RegisterMap* map) const;
+ frame sender_for_ricochet_frame(RegisterMap* map) const;
frame sender_for_interpreter_frame(RegisterMap* map) const;
frame sender_for_native_frame(RegisterMap* map) const;
@@ -400,6 +402,7 @@
// Oops-do's
void oops_compiled_arguments_do(Symbol* signature, bool has_receiver, const RegisterMap* reg_map, OopClosure* f);
void oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache = true);
+ void oops_ricochet_do(OopClosure* f, const RegisterMap* map);
private:
void oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, OopClosure* f);
@@ -508,7 +511,7 @@
// Used by frame functions to describe locations.
void describe(int owner, intptr_t* location, const char* description, int priority = 0);
- bool validate();
+ void validate();
void print();
};
--- a/hotspot/src/share/vm/runtime/globals.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/globals.hpp Tue May 24 14:32:10 2011 -0700
@@ -1460,8 +1460,10 @@
product(intx, ParallelGCBufferWastePct, 10, \
"wasted fraction of parallel allocation buffer.") \
\
- product(bool, ParallelGCRetainPLAB, true, \
- "Retain parallel allocation buffers across scavenges.") \
+ diagnostic(bool, ParallelGCRetainPLAB, false, \
+ "Retain parallel allocation buffers across scavenges; " \
+ " -- disabled because this currently conflicts with " \
+ " parallel card scanning under certain conditions ") \
\
product(intx, TargetPLABWastePct, 10, \
"target wasted space in last buffer as pct of overall allocation")\
@@ -1495,7 +1497,15 @@
product(uintx, ParGCDesiredObjsFromOverflowList, 20, \
"The desired number of objects to claim from the overflow list") \
\
- product(uintx, CMSParPromoteBlocksToClaim, 16, \
+ diagnostic(intx, ParGCStridesPerThread, 2, \
+ "The number of strides per worker thread that we divide up the " \
+ "card table scanning work into") \
+ \
+ diagnostic(intx, ParGCCardsPerStrideChunk, 256, \
+ "The number of cards in each chunk of the parallel chunks used " \
+ "during card table scanning") \
+ \
+ product(uintx, CMSParPromoteBlocksToClaim, 16, \
"Number of blocks to attempt to claim when refilling CMS LAB for "\
"parallel GC.") \
\
@@ -3708,6 +3718,10 @@
diagnostic(bool, OptimizeMethodHandles, true, \
"when constructing method handles, try to improve them") \
\
+ diagnostic(bool, UseRicochetFrames, true, \
+ "use ricochet stack frames for method handle combination, " \
+ "if the platform supports them") \
+ \
experimental(bool, TrustFinalNonStaticFields, false, \
"trust final non-static declarations for constant folding") \
\
--- a/hotspot/src/share/vm/runtime/serviceThread.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/serviceThread.cpp Tue May 24 14:32:10 2011 -0700
@@ -28,6 +28,7 @@
#include "runtime/serviceThread.hpp"
#include "runtime/mutexLocker.hpp"
#include "prims/jvmtiImpl.hpp"
+#include "services/gcNotifier.hpp"
ServiceThread* ServiceThread::_instance = NULL;
@@ -81,6 +82,7 @@
while (true) {
bool sensors_changed = false;
bool has_jvmti_events = false;
+ bool has_gc_notification_event = false;
JvmtiDeferredEvent jvmti_event;
{
// Need state transition ThreadBlockInVM so that this thread
@@ -95,9 +97,10 @@
MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
while (!(sensors_changed = LowMemoryDetector::has_pending_requests()) &&
- !(has_jvmti_events = JvmtiDeferredEventQueue::has_events())) {
+ !(has_jvmti_events = JvmtiDeferredEventQueue::has_events()) &&
+ !(has_gc_notification_event = GCNotifier::has_event())) {
// wait until one of the sensors has pending requests, or there is a
- // pending JVMTI event to post
+ // pending JVMTI event or JMX GC notification to post
Service_lock->wait(Mutex::_no_safepoint_check_flag);
}
@@ -113,6 +116,10 @@
if (sensors_changed) {
LowMemoryDetector::process_sensor_changes(jt);
}
+
+ if(has_gc_notification_event) {
+ GCNotifier::sendNotification(CHECK);
+ }
}
}
--- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp Tue May 24 14:32:10 2011 -0700
@@ -88,6 +88,8 @@
HS_DTRACE_PROBE_DECL7(hotspot, method__return, int,
char*, int, char*, int, char*, int);
+RicochetBlob* SharedRuntime::_ricochet_blob = NULL;
+
// Implementation of SharedRuntime
#ifndef PRODUCT
@@ -460,6 +462,10 @@
if (Interpreter::contains(return_address)) {
return Interpreter::rethrow_exception_entry();
}
+ // Ricochet frame unwind code
+ if (SharedRuntime::ricochet_blob() != NULL && SharedRuntime::ricochet_blob()->returns_to_bounce_addr(return_address)) {
+ return SharedRuntime::ricochet_blob()->exception_addr();
+ }
guarantee(blob == NULL || !blob->is_runtime_stub(), "caller should have skipped stub");
guarantee(!VtableStubs::contains(return_address), "NULL exceptions in vtables should have been handled already!");
@@ -1174,6 +1180,7 @@
assert(stub_frame.is_runtime_frame(), "sanity check");
frame caller_frame = stub_frame.sender(®_map);
assert(!caller_frame.is_interpreted_frame() && !caller_frame.is_entry_frame(), "unexpected frame");
+ assert(!caller_frame.is_ricochet_frame(), "unexpected frame");
#endif /* ASSERT */
methodHandle callee_method;
@@ -1222,6 +1229,7 @@
if (caller_frame.is_interpreted_frame() ||
caller_frame.is_entry_frame() ||
+ caller_frame.is_ricochet_frame() ||
is_mh_invoke_via_adapter) {
methodOop callee = thread->callee_target();
guarantee(callee != NULL && callee->is_method(), "bad handshake");
--- a/hotspot/src/share/vm/runtime/sharedRuntime.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.hpp Tue May 24 14:32:10 2011 -0700
@@ -58,6 +58,8 @@
static RuntimeStub* _resolve_virtual_call_blob;
static RuntimeStub* _resolve_static_call_blob;
+ static RicochetBlob* _ricochet_blob;
+
static SafepointBlob* _polling_page_safepoint_handler_blob;
static SafepointBlob* _polling_page_return_handler_blob;
#ifdef COMPILER2
@@ -213,6 +215,16 @@
return _resolve_static_call_blob->entry_point();
}
+ static RicochetBlob* ricochet_blob() {
+#ifdef X86
+ // Currently only implemented on x86
+ assert(!EnableInvokeDynamic || _ricochet_blob != NULL, "oops");
+#endif
+ return _ricochet_blob;
+ }
+
+ static void generate_ricochet_blob();
+
static SafepointBlob* polling_page_return_handler_blob() { return _polling_page_return_handler_blob; }
static SafepointBlob* polling_page_safepoint_handler_blob() { return _polling_page_safepoint_handler_blob; }
--- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Tue May 24 14:32:10 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. 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
--- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp Tue May 24 14:32:10 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. 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
--- a/hotspot/src/share/vm/runtime/vframeArray.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/vframeArray.cpp Tue May 24 14:32:10 2011 -0700
@@ -154,7 +154,8 @@
int unpack_counter = 0;
-void vframeArrayElement::unpack_on_stack(int callee_parameters,
+void vframeArrayElement::unpack_on_stack(int caller_actual_parameters,
+ int callee_parameters,
int callee_locals,
frame* caller,
bool is_top_frame,
@@ -270,6 +271,7 @@
temps + callee_parameters,
popframe_preserved_args_size_in_words,
locks,
+ caller_actual_parameters,
callee_parameters,
callee_locals,
caller,
@@ -415,7 +417,8 @@
}
-int vframeArrayElement::on_stack_size(int callee_parameters,
+int vframeArrayElement::on_stack_size(int caller_actual_parameters,
+ int callee_parameters,
int callee_locals,
bool is_top_frame,
int popframe_extra_stack_expression_els) const {
@@ -426,6 +429,7 @@
temps + callee_parameters,
popframe_extra_stack_expression_els,
locks,
+ caller_actual_parameters,
callee_parameters,
callee_locals,
is_top_frame);
@@ -496,7 +500,7 @@
}
}
-void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode) {
+void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode, int caller_actual_parameters) {
// stack picture
// unpack_frame
// [new interpreter frames ] (frames are skeletal but walkable)
@@ -525,7 +529,8 @@
for (index = frames() - 1; index >= 0 ; index--) {
int callee_parameters = index == 0 ? 0 : element(index-1)->method()->size_of_parameters();
int callee_locals = index == 0 ? 0 : element(index-1)->method()->max_locals();
- element(index)->unpack_on_stack(callee_parameters,
+ element(index)->unpack_on_stack(caller_actual_parameters,
+ callee_parameters,
callee_locals,
&caller_frame,
index == 0,
@@ -534,6 +539,7 @@
Deoptimization::unwind_callee_save_values(element(index)->iframe(), this);
}
caller_frame = *element(index)->iframe();
+ caller_actual_parameters = callee_parameters;
}
--- a/hotspot/src/share/vm/runtime/vframeArray.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/runtime/vframeArray.hpp Tue May 24 14:32:10 2011 -0700
@@ -83,13 +83,15 @@
// Returns the on stack word size for this frame
// callee_parameters is the number of callee locals residing inside this frame
- int on_stack_size(int callee_parameters,
+ int on_stack_size(int caller_actual_parameters,
+ int callee_parameters,
int callee_locals,
bool is_top_frame,
int popframe_extra_stack_expression_els) const;
// Unpacks the element to skeletal interpreter frame
- void unpack_on_stack(int callee_parameters,
+ void unpack_on_stack(int caller_actual_parameters,
+ int callee_parameters,
int callee_locals,
frame* caller,
bool is_top_frame,
@@ -190,7 +192,7 @@
int frame_size() const { return _frame_size; }
// Unpack the array on the stack passed in stack interval
- void unpack_to_stack(frame &unpack_frame, int exec_mode);
+ void unpack_to_stack(frame &unpack_frame, int exec_mode, int caller_actual_parameters);
// Deallocates monitor chunks allocated during deoptimization.
// This should be called when the array is not used anymore.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/gcNotifier.cpp Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "oops/oop.inline.hpp"
+#include "runtime/interfaceSupport.hpp"
+#include "runtime/java.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/mutex.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "services/gcNotifier.hpp"
+#include "services/management.hpp"
+#include "services/memoryService.hpp"
+#include "memoryManager.hpp"
+#include "memory/oopFactory.hpp"
+
+GCNotificationRequest *GCNotifier::first_request = NULL;
+GCNotificationRequest *GCNotifier::last_request = NULL;
+
+void GCNotifier::pushNotification(GCMemoryManager *mgr, const char *action, const char *cause) {
+ // Make a copy of the last GC statistics
+ // GC may occur between now and the creation of the notification
+ int num_pools = MemoryService::num_memory_pools();
+ GCStatInfo* stat = new GCStatInfo(num_pools);
+ mgr->get_last_gc_stat(stat);
+ GCNotificationRequest *request = new GCNotificationRequest(os::javaTimeMillis(),mgr,action,cause,stat);
+ addRequest(request);
+ }
+
+void GCNotifier::addRequest(GCNotificationRequest *request) {
+ MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+ if(first_request == NULL) {
+ first_request = request;
+ } else {
+ last_request->next = request;
+ }
+ last_request = request;
+ Service_lock->notify_all();
+}
+
+GCNotificationRequest *GCNotifier::getRequest() {
+ MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+ GCNotificationRequest *request = first_request;
+ if(first_request != NULL) {
+ first_request = first_request->next;
+ }
+ return request;
+}
+
+bool GCNotifier::has_event() {
+ return first_request != NULL;
+}
+
+static Handle getGcInfoBuilder(GCMemoryManager *gcManager,TRAPS) {
+
+ klassOop k = Management::sun_management_GarbageCollectorImpl_klass(CHECK_NH);
+ instanceKlassHandle gcMBeanKlass (THREAD, k);
+
+ instanceOop i = gcManager->get_memory_manager_instance(THREAD);
+ instanceHandle ih(THREAD, i);
+
+ JavaValue result(T_OBJECT);
+ JavaCallArguments args(ih);
+
+ JavaCalls::call_virtual(&result,
+ gcMBeanKlass,
+ vmSymbols::getGcInfoBuilder_name(),
+ vmSymbols::getGcInfoBuilder_signature(),
+ &args,
+ CHECK_NH);
+ return Handle(THREAD,(oop)result.get_jobject());
+
+}
+
+static Handle createGcInfo(GCMemoryManager *gcManager, GCStatInfo *gcStatInfo,TRAPS) {
+
+ // Fill the arrays of MemoryUsage objects with before and after GC
+ // per pool memory usage
+
+ klassOop muKlass = Management::java_lang_management_MemoryUsage_klass(CHECK_NH); objArrayOop bu = oopFactory::new_objArray( muKlass,MemoryService::num_memory_pools(), CHECK_NH);
+ objArrayHandle usage_before_gc_ah(THREAD, bu);
+ objArrayOop au = oopFactory::new_objArray(muKlass,MemoryService::num_memory_pools(), CHECK_NH);
+ objArrayHandle usage_after_gc_ah(THREAD, au);
+
+ for (int i = 0; i < MemoryService::num_memory_pools(); i++) {
+ Handle before_usage = MemoryService::create_MemoryUsage_obj(gcStatInfo->before_gc_usage_for_pool(i), CHECK_NH);
+ Handle after_usage;
+
+ MemoryUsage u = gcStatInfo->after_gc_usage_for_pool(i);
+ if (u.max_size() == 0 && u.used() > 0) {
+ // If max size == 0, this pool is a survivor space.
+ // Set max size = -1 since the pools will be swapped after GC.
+ MemoryUsage usage(u.init_size(), u.used(), u.committed(), (size_t)-1);
+ after_usage = MemoryService::create_MemoryUsage_obj(usage, CHECK_NH);
+ } else {
+ after_usage = MemoryService::create_MemoryUsage_obj(u, CHECK_NH);
+ }
+ usage_before_gc_ah->obj_at_put(i, before_usage());
+ usage_after_gc_ah->obj_at_put(i, after_usage());
+ }
+
+ // Current implementation only has 1 attribute (number of GC threads)
+ // The type is 'I'
+ objArrayOop extra_args_array = oopFactory::new_objArray(SystemDictionary::Integer_klass(), 1, CHECK_NH);
+ objArrayHandle extra_array (THREAD, extra_args_array);
+ klassOop itKlass= SystemDictionary::Integer_klass();
+ instanceKlassHandle intK(THREAD, itKlass);
+
+ instanceHandle extra_arg_val = intK->allocate_instance_handle(CHECK_NH);
+
+ {
+ JavaValue res(T_VOID);
+ JavaCallArguments argsInt;
+ argsInt.push_oop(extra_arg_val);
+ argsInt.push_int(gcManager->num_gc_threads());
+
+ JavaCalls::call_special(&res,
+ intK,
+ vmSymbols::object_initializer_name(),
+ vmSymbols::int_void_signature(),
+ &argsInt,
+ CHECK_NH);
+ }
+ extra_array->obj_at_put(0,extra_arg_val());
+
+ klassOop gcInfoklass = Management::com_sun_management_GcInfo_klass(CHECK_NH);
+ instanceKlassHandle ik (THREAD,gcInfoklass);
+
+ Handle gcInfo_instance = ik->allocate_instance_handle(CHECK_NH);
+
+ JavaValue constructor_result(T_VOID);
+ JavaCallArguments constructor_args(16);
+ constructor_args.push_oop(gcInfo_instance);
+ constructor_args.push_oop(getGcInfoBuilder(gcManager,THREAD));
+ constructor_args.push_long(gcStatInfo->gc_index());
+ constructor_args.push_long(gcStatInfo->start_time());
+ constructor_args.push_long(gcStatInfo->end_time());
+ constructor_args.push_oop(usage_before_gc_ah);
+ constructor_args.push_oop(usage_after_gc_ah);
+ constructor_args.push_oop(extra_array);
+
+ JavaCalls::call_special(&constructor_result,
+ ik,
+ vmSymbols::object_initializer_name(),
+ vmSymbols::com_sun_management_GcInfo_constructor_signature(),
+ &constructor_args,
+ CHECK_NH);
+
+ return Handle(gcInfo_instance());
+}
+
+void GCNotifier::sendNotification(TRAPS) {
+ ResourceMark rm(THREAD);
+ GCNotificationRequest *request = getRequest();
+ if(request != NULL) {
+ Handle objGcInfo = createGcInfo(request->gcManager,request->gcStatInfo,THREAD);
+
+ Handle objName = java_lang_String::create_from_platform_dependent_str(request->gcManager->name(), CHECK);
+ Handle objAction = java_lang_String::create_from_platform_dependent_str(request->gcAction, CHECK);
+ Handle objCause = java_lang_String::create_from_platform_dependent_str(request->gcCause, CHECK);
+
+ klassOop k = Management::sun_management_GarbageCollectorImpl_klass(CHECK);
+ instanceKlassHandle gc_mbean_klass (THREAD, k);
+
+ instanceOop gc_mbean = request->gcManager->get_memory_manager_instance(THREAD);
+ instanceHandle gc_mbean_h(THREAD, gc_mbean);
+ if (!gc_mbean_h->is_a(k)) {
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
+ "This GCMemoryManager doesn't have a GarbageCollectorMXBean");
+ }
+
+ JavaValue result(T_VOID);
+ JavaCallArguments args(gc_mbean_h);
+ args.push_long(request->timestamp);
+ args.push_oop(objName);
+ args.push_oop(objAction);
+ args.push_oop(objCause);
+ args.push_oop(objGcInfo);
+
+ JavaCalls::call_virtual(&result,
+ gc_mbean_klass,
+ vmSymbols::createGCNotification_name(),
+ vmSymbols::createGCNotification_signature(),
+ &args,
+ CHECK);
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ }
+
+ delete request;
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/services/gcNotifier.hpp Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_SERVICES_GCNOTIFIER_HPP
+#define SHARE_VM_SERVICES_GCNOTIFIER_HPP
+
+#include "memory/allocation.hpp"
+#include "services/memoryPool.hpp"
+#include "services/memoryService.hpp"
+#include "services/memoryManager.hpp"
+
+class GCNotificationRequest : public CHeapObj {
+ friend class GCNotifier;
+ GCNotificationRequest *next;
+ jlong timestamp;
+ GCMemoryManager *gcManager;
+ const char *gcAction;
+ const char *gcCause;
+ GCStatInfo *gcStatInfo;
+public:
+ GCNotificationRequest(jlong ts, GCMemoryManager *manager, const char*action, const char *cause,GCStatInfo *info) {
+ next = NULL;
+ timestamp = ts;
+ gcManager = manager;
+ gcAction = action;
+ gcCause = cause;
+ gcStatInfo = info;
+ }
+
+ ~GCNotificationRequest() {
+ delete gcStatInfo;
+ }
+};
+
+class GCNotifier : public AllStatic {
+ friend class ServiceThread;
+private:
+ static GCNotificationRequest *first_request;
+ static GCNotificationRequest *last_request;
+ static void addRequest(GCNotificationRequest *request);
+ static GCNotificationRequest *getRequest();
+public:
+ static void pushNotification(GCMemoryManager *manager, const char *action, const char *cause);
+ static bool has_event();
+ static void sendNotification(TRAPS);
+};
+
+#endif // SHARE_VM_SERVICES_GCNOTIFIER_HPP
--- a/hotspot/src/share/vm/services/heapDumper.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/services/heapDumper.cpp Tue May 24 14:32:10 2011 -0700
@@ -1649,6 +1649,9 @@
if (fr->is_entry_frame()) {
last_entry_frame = fr;
}
+ if (fr->is_ricochet_frame()) {
+ fr->oops_ricochet_do(&blk, vf->register_map());
+ }
}
vf = vf->sender();
}
--- a/hotspot/src/share/vm/services/jmm.h Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/services/jmm.h Tue May 24 14:32:10 2011 -0700
@@ -48,7 +48,7 @@
JMM_VERSION_1_0 = 0x20010000,
JMM_VERSION_1_1 = 0x20010100, // JDK 6
JMM_VERSION_1_2 = 0x20010200, // JDK 7
- JMM_VERSION = 0x20010200
+ JMM_VERSION = 0x20010201
};
typedef struct {
@@ -293,6 +293,9 @@
jlongArray ids,
jboolean lockedMonitors,
jboolean lockedSynchronizers);
+ void (JNICALL *SetGCNotificationEnabled) (JNIEnv *env,
+ jobject mgr,
+ jboolean enabled);
} JmmInterface;
#ifdef __cplusplus
--- a/hotspot/src/share/vm/services/management.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/services/management.cpp Tue May 24 14:32:10 2011 -0700
@@ -42,6 +42,7 @@
#include "services/classLoadingService.hpp"
#include "services/heapDumper.hpp"
#include "services/lowMemoryDetector.hpp"
+#include "services/gcNotifier.hpp"
#include "services/management.hpp"
#include "services/memoryManager.hpp"
#include "services/memoryPool.hpp"
@@ -60,6 +61,8 @@
klassOop Management::_memoryManagerMXBean_klass = NULL;
klassOop Management::_garbageCollectorMXBean_klass = NULL;
klassOop Management::_managementFactory_klass = NULL;
+klassOop Management::_garbageCollectorImpl_klass = NULL;
+klassOop Management::_gcInfo_klass = NULL;
jmmOptionalSupport Management::_optional_support = {0};
TimeStamp Management::_stamp;
@@ -179,6 +182,8 @@
f->do_oop((oop*) &_memoryManagerMXBean_klass);
f->do_oop((oop*) &_garbageCollectorMXBean_klass);
f->do_oop((oop*) &_managementFactory_klass);
+ f->do_oop((oop*) &_garbageCollectorImpl_klass);
+ f->do_oop((oop*) &_gcInfo_klass);
}
klassOop Management::java_lang_management_ThreadInfo_klass(TRAPS) {
@@ -230,6 +235,20 @@
return _managementFactory_klass;
}
+klassOop Management::sun_management_GarbageCollectorImpl_klass(TRAPS) {
+ if (_garbageCollectorImpl_klass == NULL) {
+ _garbageCollectorImpl_klass = load_and_initialize_klass(vmSymbols::sun_management_GarbageCollectorImpl(), CHECK_NULL);
+ }
+ return _garbageCollectorImpl_klass;
+}
+
+klassOop Management::com_sun_management_GcInfo_klass(TRAPS) {
+ if (_gcInfo_klass == NULL) {
+ _gcInfo_klass = load_and_initialize_klass(vmSymbols::com_sun_management_GcInfo(), CHECK_NULL);
+ }
+ return _gcInfo_klass;
+}
+
static void initialize_ThreadInfo_constructor_arguments(JavaCallArguments* args, ThreadSnapshot* snapshot, TRAPS) {
Handle snapshot_thread(THREAD, snapshot->threadObj());
@@ -2056,6 +2075,13 @@
}
JVM_END
+JVM_ENTRY(void, jmm_SetGCNotificationEnabled(JNIEnv *env, jobject obj, jboolean enabled))
+ ResourceMark rm(THREAD);
+ // Get the GCMemoryManager
+ GCMemoryManager* mgr = get_gc_memory_manager_from_jobject(obj, CHECK);
+ mgr->set_notification_enabled(enabled?true:false);
+JVM_END
+
// Dump heap - Returns 0 if succeeds.
JVM_ENTRY(jint, jmm_DumpHeap0(JNIEnv *env, jstring outputfile, jboolean live))
#ifndef SERVICES_KERNEL
@@ -2122,7 +2148,8 @@
jmm_FindDeadlockedThreads,
jmm_SetVMGlobal,
NULL,
- jmm_DumpThreads
+ jmm_DumpThreads,
+ jmm_SetGCNotificationEnabled
};
void* Management::get_jmm_interface(int version) {
--- a/hotspot/src/share/vm/services/management.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/services/management.hpp Tue May 24 14:32:10 2011 -0700
@@ -49,6 +49,8 @@
static klassOop _memoryManagerMXBean_klass;
static klassOop _garbageCollectorMXBean_klass;
static klassOop _managementFactory_klass;
+ static klassOop _garbageCollectorImpl_klass;
+ static klassOop _gcInfo_klass;
static klassOop load_and_initialize_klass(Symbol* sh, TRAPS);
@@ -86,6 +88,8 @@
static klassOop java_lang_management_GarbageCollectorMXBean_klass(TRAPS);
static klassOop sun_management_Sensor_klass(TRAPS);
static klassOop sun_management_ManagementFactory_klass(TRAPS);
+ static klassOop sun_management_GarbageCollectorImpl_klass(TRAPS);
+ static klassOop com_sun_management_GcInfo_klass(TRAPS);
static instanceOop create_thread_info_instance(ThreadSnapshot* snapshot, TRAPS);
static instanceOop create_thread_info_instance(ThreadSnapshot* snapshot, objArrayHandle monitors_array, typeArrayHandle depths_array, objArrayHandle synchronizers_array, TRAPS);
--- a/hotspot/src/share/vm/services/memoryManager.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/services/memoryManager.cpp Tue May 24 14:32:10 2011 -0700
@@ -33,6 +33,7 @@
#include "services/memoryManager.hpp"
#include "services/memoryPool.hpp"
#include "services/memoryService.hpp"
+#include "services/gcNotifier.hpp"
#include "utilities/dtrace.hpp"
HS_DTRACE_PROBE_DECL8(hotspot, mem__pool__gc__begin, char*, int, char*, int,
@@ -202,6 +203,7 @@
_last_gc_lock = new Mutex(Mutex::leaf, "_last_gc_lock", true);
_current_gc_stat = NULL;
_num_gc_threads = 1;
+ _notification_enabled = false;
}
GCMemoryManager::~GCMemoryManager() {
@@ -250,7 +252,8 @@
// to ensure the current gc stat is placed in _last_gc_stat.
void GCMemoryManager::gc_end(bool recordPostGCUsage,
bool recordAccumulatedGCTime,
- bool recordGCEndTime, bool countCollection) {
+ bool recordGCEndTime, bool countCollection,
+ GCCause::Cause cause) {
if (recordAccumulatedGCTime) {
_accumulated_timer.stop();
}
@@ -283,6 +286,11 @@
pool->set_last_collection_usage(usage);
LowMemoryDetector::detect_after_gc_memory(pool);
}
+ if(is_notification_enabled()) {
+ bool isMajorGC = this == MemoryService::get_major_gc_manager();
+ GCNotifier::pushNotification(this, isMajorGC ? "end of major GC" : "end of minor GC",
+ GCCause::to_string(cause));
+ }
}
if (countCollection) {
_num_collections++;
--- a/hotspot/src/share/vm/services/memoryManager.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/services/memoryManager.hpp Tue May 24 14:32:10 2011 -0700
@@ -166,6 +166,7 @@
Mutex* _last_gc_lock;
GCStatInfo* _current_gc_stat;
int _num_gc_threads;
+ volatile bool _notification_enabled;
public:
GCMemoryManager();
~GCMemoryManager();
@@ -181,7 +182,7 @@
void gc_begin(bool recordGCBeginTime, bool recordPreGCUsage,
bool recordAccumulatedGCTime);
void gc_end(bool recordPostGCUsage, bool recordAccumulatedGCTime,
- bool recordGCEndTime, bool countCollection);
+ bool recordGCEndTime, bool countCollection, GCCause::Cause cause);
void reset_gc_stat() { _num_collections = 0; _accumulated_timer.reset(); }
@@ -189,6 +190,8 @@
// the collection count. Zero signifies no gc has taken place.
size_t get_last_gc_stat(GCStatInfo* dest);
+ void set_notification_enabled(bool enabled) { _notification_enabled = enabled; }
+ bool is_notification_enabled() { return _notification_enabled; }
virtual MemoryManager::Name kind() = 0;
};
--- a/hotspot/src/share/vm/services/memoryService.cpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/services/memoryService.cpp Tue May 24 14:32:10 2011 -0700
@@ -565,7 +565,8 @@
void MemoryService::gc_end(bool fullGC, bool recordPostGCUsage,
bool recordAccumulatedGCTime,
- bool recordGCEndTime, bool countCollection) {
+ bool recordGCEndTime, bool countCollection,
+ GCCause::Cause cause) {
GCMemoryManager* mgr;
if (fullGC) {
@@ -577,7 +578,7 @@
// register the GC end statistics and memory usage
mgr->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
- countCollection);
+ countCollection, cause);
}
void MemoryService::oops_do(OopClosure* f) {
@@ -633,7 +634,7 @@
// gc manager (so _fullGC is set to false ) and for other generation kinds
// doing mark-sweep-compact uses major gc manager (so _fullGC is set
// to true).
-TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind) {
+TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind, GCCause::Cause cause) {
switch (kind) {
case Generation::DefNew:
#ifndef SERIALGC
@@ -654,9 +655,10 @@
}
// this has to be called in a stop the world pause and represent
// an entire gc pause, start to finish:
- initialize(_fullGC, true, true, true, true, true, true, true);
+ initialize(_fullGC, cause,true, true, true, true, true, true, true);
}
TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC,
+ GCCause::Cause cause,
bool recordGCBeginTime,
bool recordPreGCUsage,
bool recordPeakUsage,
@@ -664,7 +666,7 @@
bool recordAccumulatedGCTime,
bool recordGCEndTime,
bool countCollection) {
- initialize(fullGC, recordGCBeginTime, recordPreGCUsage, recordPeakUsage,
+ initialize(fullGC, cause, recordGCBeginTime, recordPreGCUsage, recordPeakUsage,
recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
countCollection);
}
@@ -672,6 +674,7 @@
// for a subclass to create then initialize an instance before invoking
// the MemoryService
void TraceMemoryManagerStats::initialize(bool fullGC,
+ GCCause::Cause cause,
bool recordGCBeginTime,
bool recordPreGCUsage,
bool recordPeakUsage,
@@ -687,6 +690,7 @@
_recordAccumulatedGCTime = recordAccumulatedGCTime;
_recordGCEndTime = recordGCEndTime;
_countCollection = countCollection;
+ _cause = cause;
MemoryService::gc_begin(_fullGC, _recordGCBeginTime, _recordAccumulatedGCTime,
_recordPreGCUsage, _recordPeakUsage);
@@ -694,6 +698,6 @@
TraceMemoryManagerStats::~TraceMemoryManagerStats() {
MemoryService::gc_end(_fullGC, _recordPostGCUsage, _recordAccumulatedGCTime,
- _recordGCEndTime, _countCollection);
+ _recordGCEndTime, _countCollection, _cause);
}
--- a/hotspot/src/share/vm/services/memoryService.hpp Tue May 24 14:15:14 2011 -0700
+++ b/hotspot/src/share/vm/services/memoryService.hpp Tue May 24 14:32:10 2011 -0700
@@ -29,6 +29,7 @@
#include "memory/generation.hpp"
#include "runtime/handles.hpp"
#include "services/memoryUsage.hpp"
+#include "gc_interface/gcCause.hpp"
// Forward declaration
class MemoryPool;
@@ -162,7 +163,8 @@
bool recordPreGCUsage, bool recordPeakUsage);
static void gc_end(bool fullGC, bool recordPostGCUsage,
bool recordAccumulatedGCTime,
- bool recordGCEndTime, bool countCollection);
+ bool recordGCEndTime, bool countCollection,
+ GCCause::Cause cause);
static void oops_do(OopClosure* f);
@@ -172,6 +174,14 @@
// Create an instance of java/lang/management/MemoryUsage
static Handle create_MemoryUsage_obj(MemoryUsage usage, TRAPS);
+
+ static const GCMemoryManager* get_minor_gc_manager() {
+ return _minor_gc_manager;
+ }
+
+ static const GCMemoryManager* get_major_gc_manager() {
+ return _major_gc_manager;
+ }
};
class TraceMemoryManagerStats : public StackObj {
@@ -184,10 +194,11 @@
bool _recordAccumulatedGCTime;
bool _recordGCEndTime;
bool _countCollection;
-
+ GCCause::Cause _cause;
public:
TraceMemoryManagerStats() {}
TraceMemoryManagerStats(bool fullGC,
+ GCCause::Cause cause,
bool recordGCBeginTime = true,
bool recordPreGCUsage = true,
bool recordPeakUsage = true,
@@ -197,6 +208,7 @@
bool countCollection = true);
void initialize(bool fullGC,
+ GCCause::Cause cause,
bool recordGCBeginTime,
bool recordPreGCUsage,
bool recordPeakUsage,
@@ -205,7 +217,7 @@
bool recordGCEndTime,
bool countCollection);
- TraceMemoryManagerStats(Generation::Name kind);
+ TraceMemoryManagerStats(Generation::Name kind, GCCause::Cause cause);
~TraceMemoryManagerStats();
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/7042153/Test7042153.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @bug 7042153
+ * @summary Bad folding of IfOps with unloaded constant arguments in C1
+ *
+ * @run main/othervm -Xcomp Test7042153
+ */
+
+import java.lang.reflect.*;
+
+public class Test7042153 {
+ static public class Bar { }
+ static public class Foo { }
+
+ static volatile boolean z;
+ public static void main(String [] args) {
+ Class cx = Bar.class;
+ Class cy = Foo.class;
+ z = (cx == cy);
+ }
+}
--- a/jaxp/.hgtags Tue May 24 14:15:14 2011 -0700
+++ b/jaxp/.hgtags Tue May 24 14:32:10 2011 -0700
@@ -116,3 +116,4 @@
28c7c0ed2444607829ba11ad827f8d52197a2830 jdk7-b139
c8136fd161c83917f87e93b14fa2ba3483f9be83 jdk7-b140
e1b5ef243445bf836d095fd44866e1771ef99374 jdk7-b141
+7d067af4b25e4b7e6b28bef48527d67f8650e6c5 jdk7-b142
--- a/jaxws/.hgtags Tue May 24 14:15:14 2011 -0700
+++ b/jaxws/.hgtags Tue May 24 14:32:10 2011 -0700
@@ -116,3 +116,4 @@
c025078c8362076503bb83b8e4da14ba7b347940 jdk7-b139
82a9022c4f21b1313023c8303b557a17c4106701 jdk7-b140
66826b0aec5a1834da3821c35cf85ac154e9b04d jdk7-b141
+0ef3ef823c39eee3d670e58027c3219cb66f0283 jdk7-b142
--- a/jaxws/jaxws.properties Tue May 24 14:15:14 2011 -0700
+++ b/jaxws/jaxws.properties Tue May 24 14:32:10 2011 -0700
@@ -25,8 +25,8 @@
drops.master.copy.base=${drops.dir}
-jaxws_src.bundle.name=jdk7-jaxws2_2_4-b01-2011_04_08.zip
-jaxws_src.bundle.md5.checksum=9f35dd731c99ddb62db650aaf20e5bf4
+jaxws_src.bundle.name=jdk7-jaxws2_2_4-b02-2011_05_09.zip
+jaxws_src.bundle.md5.checksum=0972a58090e8b2735d91a2f5d1ae1964
jaxws_src.master.bundle.dir=${drops.master.copy.base}
jaxws_src.master.bundle.url.base=http://download.java.net/glassfish/components/jax-ws/openjdk/jdk7
--- a/jdk/.hgtags Tue May 24 14:15:14 2011 -0700
+++ b/jdk/.hgtags Tue May 24 14:32:10 2011 -0700
@@ -117,3 +117,4 @@
9315c733fb17ddfb9fb44be7e0ffea37bf3c727d jdk7-b140
63eeefe118da18c75ba3d36266768cd1ccaaca6b jdk7-b141
312612e89ece62633f4809706dec00bcd5fe7c2d jdk7-b142
+efbf75c24b0f31847c9c403f6dc07dc80551908d jdk7-b143
--- a/jdk/make/sun/rmi/rmi/Makefile Tue May 24 14:15:14 2011 -0700
+++ b/jdk/make/sun/rmi/rmi/Makefile Tue May 24 14:32:10 2011 -0700
@@ -85,16 +85,21 @@
sun.rmi.registry.RegistryImpl \
sun.rmi.transport.DGCImpl
-ifeq ($(PLATFORM), windows)
-build: stubs
-else # PLATFORM
-ifneq ($(ARCH_DATA_MODEL), 32)
-build: stubs
-else # ARCH_DATA_MODEL
-build: stubs bin
+#
+# The java-rmi.cgi script in bin/ only gets delivered in certain situations
+#
+BUILD_TARGETS = stubs
+ifeq ($(PLATFORM), linux)
+ BUILD_TARGETS += bin
endif
+ifeq ($(PLATFORM), solaris)
+ ifeq ($(ARCH_DATA_MODEL), 32)
+ BUILD_TARGETS += bin
+ endif
endif
+build: $(BUILD_TARGETS)
+
clean clobber:: bin.clean
--- a/jdk/make/sun/xawt/mapfile-vers Tue May 24 14:15:14 2011 -0700
+++ b/jdk/make/sun/xawt/mapfile-vers Tue May 24 14:32:10 2011 -0700
@@ -158,7 +158,6 @@
Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl;
Java_sun_awt_X11_XRobotPeer_mouseWheelImpl;
Java_sun_awt_X11_XRobotPeer_setup;
- Java_sun_awt_X11_XRobotPeer__1dispose;
Java_sun_awt_X11_XToolkit_getNumberOfButtonsImpl;
Java_java_awt_Component_initIDs;
Java_java_awt_Container_initIDs;
--- a/jdk/src/share/classes/java/awt/Component.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/awt/Component.java Tue May 24 14:32:10 2011 -0700
@@ -2887,11 +2887,12 @@
/**
* Invalidates this component and its ancestors.
* <p>
- * All the ancestors of this component up to the nearest validate root are
- * marked invalid also. If there is no a validate root container for this
- * component, all of its ancestors up to the root of the hierarchy are
- * marked invalid as well. Marking a container <i>invalid</i> indicates
- * that the container needs to be laid out.
+ * By default, all the ancestors of the component up to the top-most
+ * container of the hierarchy are marked invalid. If the {@code
+ * java.awt.smartInvalidate} system property is set to {@code true},
+ * invalidation stops on the nearest validate root of this component.
+ * Marking a container <i>invalid</i> indicates that the container needs to
+ * be laid out.
* <p>
* This method is called automatically when any layout-related information
* changes (e.g. setting the bounds of the component, or adding the
--- a/jdk/src/share/classes/java/awt/Container.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/awt/Container.java Tue May 24 14:32:10 2011 -0700
@@ -41,6 +41,8 @@
import java.io.PrintStream;
import java.io.PrintWriter;
+import java.security.AccessController;
+
import java.util.Arrays;
import java.util.EventListener;
import java.util.HashSet;
@@ -60,6 +62,8 @@
import sun.java2d.pipe.Region;
+import sun.security.action.GetBooleanAction;
+
/**
* A generic Abstract Window Toolkit(AWT) container object is a component
* that can contain other AWT components.
@@ -1506,12 +1510,18 @@
* Layout-related changes, such as bounds of the validate root descendants,
* do not affect the layout of the validate root parent. This peculiarity
* enables the {@code invalidate()} method to stop invalidating the
- * component hierarchy when the method encounters a validate root.
+ * component hierarchy when the method encounters a validate root. However,
+ * to preserve backward compatibility this new optimized behavior is
+ * enabled only when the {@code java.awt.smartInvalidate} system property
+ * value is set to {@code true}.
* <p>
- * If a component hierarchy contains validate roots, the {@code validate()}
- * method must be invoked on the validate root of a previously invalidated
- * component, rather than on the top-level container (such as a {@code
- * Frame} object) to restore the validity of the hierarchy later.
+ * If a component hierarchy contains validate roots and the new optimized
+ * {@code invalidate()} behavior is enabled, the {@code validate()} method
+ * must be invoked on the validate root of a previously invalidated
+ * component to restore the validity of the hierarchy later. Otherwise,
+ * calling the {@code validate()} method on the top-level container (such
+ * as a {@code Frame} object) should be used to restore the validity of the
+ * component hierarchy.
* <p>
* The {@code Window} class and the {@code Applet} class are the validate
* roots in AWT. Swing introduces more validate roots.
@@ -1527,13 +1537,20 @@
return false;
}
+ private static final boolean isJavaAwtSmartInvalidate;
+ static {
+ // Don't lazy-read because every app uses invalidate()
+ isJavaAwtSmartInvalidate = AccessController.doPrivileged(
+ new GetBooleanAction("java.awt.smartInvalidate"));
+ }
+
/**
* Invalidates the parent of the container unless the container
* is a validate root.
*/
@Override
void invalidateParent() {
- if (!isValidateRoot()) {
+ if (!isJavaAwtSmartInvalidate || !isValidateRoot()) {
super.invalidateParent();
}
}
@@ -1572,9 +1589,8 @@
* automatically. Note that the ancestors of the container may be
* invalidated also (see {@link Component#invalidate} for details.)
* Therefore, to restore the validity of the hierarchy, the {@code
- * validate()} method should be invoked on a validate root of an
- * invalidated component, or on the top-most container if the hierarchy
- * does not contain validate roots.
+ * validate()} method should be invoked on the top-most invalid
+ * container of the hierarchy.
* <p>
* Validating the container may be a quite time-consuming operation. For
* performance reasons a developer may postpone the validation of the
--- a/jdk/src/share/classes/java/awt/Toolkit.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/awt/Toolkit.java Tue May 24 14:32:10 2011 -0700
@@ -466,10 +466,7 @@
*/
protected void loadSystemColors(int[] systemColors)
throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
-
+ GraphicsEnvironment.checkHeadless();
}
/**
@@ -504,10 +501,7 @@
*/
public void setDynamicLayout(boolean dynamic)
throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
-
+ GraphicsEnvironment.checkHeadless();
}
/**
@@ -531,9 +525,8 @@
*/
protected boolean isDynamicLayoutSet()
throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
+ GraphicsEnvironment.checkHeadless();
+
if (this != Toolkit.getDefaultToolkit()) {
return Toolkit.getDefaultToolkit().isDynamicLayoutSet();
} else {
@@ -569,9 +562,8 @@
*/
public boolean isDynamicLayoutActive()
throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
+ GraphicsEnvironment.checkHeadless();
+
if (this != Toolkit.getDefaultToolkit()) {
return Toolkit.getDefaultToolkit().isDynamicLayoutActive();
} else {
@@ -615,9 +607,7 @@
*/
public Insets getScreenInsets(GraphicsConfiguration gc)
throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
+ GraphicsEnvironment.checkHeadless();
if (this != Toolkit.getDefaultToolkit()) {
return Toolkit.getDefaultToolkit().getScreenInsets(gc);
} else {
@@ -1200,10 +1190,7 @@
* security manager's <code>checkPermission</code> method with a <code>
* RuntimePermission("queuePrintJob")</code> permission.
*
- * @param frame the parent of the print dialog. May be null if and only
- * if jobAttributes is not null and jobAttributes.getDialog()
- * returns JobAttributes.DialogType.NONE or
- * JobAttributes.DialogType.COMMON.
+ * @param frame the parent of the print dialog. May not be null.
* @param jobtitle the title of the PrintJob. A null title is equivalent
* to "".
* @param jobAttributes a set of job attributes which will control the
@@ -1359,9 +1346,8 @@
* @since 1.4
*/
public Clipboard getSystemSelection() throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
+ GraphicsEnvironment.checkHeadless();
+
if (this != Toolkit.getDefaultToolkit()) {
return Toolkit.getDefaultToolkit().getSystemSelection();
} else {
@@ -1391,9 +1377,7 @@
* @since JDK1.1
*/
public int getMenuShortcutKeyMask() throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
+ GraphicsEnvironment.checkHeadless();
return Event.CTRL_MASK;
}
@@ -1418,7 +1402,10 @@
* @since 1.3
*/
public boolean getLockingKeyState(int keyCode)
- throws UnsupportedOperationException {
+ throws UnsupportedOperationException
+ {
+ GraphicsEnvironment.checkHeadless();
+
if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK ||
keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) {
throw new IllegalArgumentException("invalid key for Toolkit.getLockingKeyState");
@@ -1449,7 +1436,10 @@
* @since 1.3
*/
public void setLockingKeyState(int keyCode, boolean on)
- throws UnsupportedOperationException {
+ throws UnsupportedOperationException
+ {
+ GraphicsEnvironment.checkHeadless();
+
if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK ||
keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) {
throw new IllegalArgumentException("invalid key for Toolkit.setLockingKeyState");
@@ -1523,9 +1513,8 @@
*/
public Dimension getBestCursorSize(int preferredWidth,
int preferredHeight) throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
+ GraphicsEnvironment.checkHeadless();
+
// Override to implement custom cursor support.
if (this != Toolkit.getDefaultToolkit()) {
return Toolkit.getDefaultToolkit().
@@ -1553,9 +1542,8 @@
* @since 1.2
*/
public int getMaximumCursorColors() throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
+ GraphicsEnvironment.checkHeadless();
+
// Override to implement custom cursor support.
if (this != Toolkit.getDefaultToolkit()) {
return Toolkit.getDefaultToolkit().getMaximumCursorColors();
@@ -1605,9 +1593,8 @@
public boolean isFrameStateSupported(int state)
throws HeadlessException
{
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
+ GraphicsEnvironment.checkHeadless();
+
if (this != Toolkit.getDefaultToolkit()) {
return Toolkit.getDefaultToolkit().
isFrameStateSupported(state);
@@ -2614,9 +2601,8 @@
* @since 1.7
*/
public boolean areExtraMouseButtonsEnabled() throws HeadlessException {
- if (GraphicsEnvironment.isHeadless()){
- throw new HeadlessException();
- }
+ GraphicsEnvironment.checkHeadless();
+
return Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled();
}
}
--- a/jdk/src/share/classes/java/lang/Throwable.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/Throwable.java Tue May 24 14:32:10 2011 -0700
@@ -777,7 +777,8 @@
* @see java.lang.Throwable#printStackTrace()
*/
public synchronized Throwable fillInStackTrace() {
- if (stackTrace != null) {
+ if (stackTrace != null ||
+ backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
@@ -817,7 +818,8 @@
private synchronized StackTraceElement[] getOurStackTrace() {
// Initialize stack trace field with information from
// backtrace if this is the first call to this method
- if (stackTrace == UNASSIGNED_STACK) {
+ if (stackTrace == UNASSIGNED_STACK ||
+ (stackTrace == null && backtrace != null) /* Out of protocol state */) {
int depth = getStackTraceDepth();
stackTrace = new StackTraceElement[depth];
for (int i=0; i < depth; i++)
@@ -865,7 +867,8 @@
}
synchronized (this) {
- if (this.stackTrace == null) // Immutable stack
+ if (this.stackTrace == null && // Immutable stack
+ backtrace == null) // Test for out of protocol state
return;
this.stackTrace = defensiveCopy;
}
--- a/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/AdapterMethodHandle.java Tue May 24 14:32:10 2011 -0700
@@ -27,6 +27,7 @@
import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
+import sun.invoke.util.ValueConversions;
import java.util.Arrays;
import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.*;
@@ -55,29 +56,35 @@
this(target, newType, conv, null);
}
+ int getConversion() { return conversion; }
+
// TO DO: When adapting another MH with a null conversion, clone
// the target and change its type, instead of adding another layer.
/** Can a JVM-level adapter directly implement the proposed
* argument conversions, as if by MethodHandles.convertArguments?
*/
- static boolean canPairwiseConvert(MethodType newType, MethodType oldType) {
+ static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) {
// same number of args, of course
int len = newType.parameterCount();
if (len != oldType.parameterCount())
return false;
- // Check return type. (Not much can be done with it.)
+ // Check return type.
Class<?> exp = newType.returnType();
Class<?> ret = oldType.returnType();
- if (!VerifyType.isNullConversion(ret, exp))
- return false;
+ if (!VerifyType.isNullConversion(ret, exp)) {
+ if (!convOpSupported(OP_COLLECT_ARGS))
+ return false;
+ if (!canConvertArgument(ret, exp, level))
+ return false;
+ }
// Check args pairwise.
for (int i = 0; i < len; i++) {
Class<?> src = newType.parameterType(i); // source type
Class<?> dst = oldType.parameterType(i); // destination type
- if (!canConvertArgument(src, dst))
+ if (!canConvertArgument(src, dst, level))
return false;
}
@@ -87,11 +94,14 @@
/** Can a JVM-level adapter directly implement the proposed
* argument conversion, as if by MethodHandles.convertArguments?
*/
- static boolean canConvertArgument(Class<?> src, Class<?> dst) {
+ static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
// ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
// so we don't need to repeat so much decision making.
if (VerifyType.isNullConversion(src, dst)) {
return true;
+ } else if (convOpSupported(OP_COLLECT_ARGS)) {
+ // If we can build filters, we can convert anything to anything.
+ return true;
} else if (src.isPrimitive()) {
if (dst.isPrimitive())
return canPrimCast(src, dst);
@@ -99,7 +109,7 @@
return canBoxArgument(src, dst);
} else {
if (dst.isPrimitive())
- return canUnboxArgument(src, dst);
+ return canUnboxArgument(src, dst, level);
else
return true; // any two refs can be interconverted
}
@@ -109,21 +119,20 @@
* Create a JVM-level adapter method handle to conform the given method
* handle to the similar newType, using only pairwise argument conversions.
* For each argument, convert incoming argument to the exact type needed.
- * Only null conversions are allowed on the return value (until
- * the JVM supports ricochet adapters).
- * The argument conversions allowed are casting, unboxing,
+ * The argument conversions allowed are casting, boxing and unboxing,
* integral widening or narrowing, and floating point widening or narrowing.
* @param newType required call type
* @param target original method handle
+ * @param level which strength of conversion is allowed
* @return an adapter to the original handle with the desired new type,
* or the original target if the types are already identical
* or null if the adaptation cannot be made
*/
- static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target) {
+ static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target, int level) {
MethodType oldType = target.type();
if (newType == oldType) return target;
- if (!canPairwiseConvert(newType, oldType))
+ if (!canPairwiseConvert(newType, oldType, level))
return null;
// (after this point, it is an assertion error to fail to convert)
@@ -138,9 +147,14 @@
break;
}
}
+
+ Class<?> needReturn = newType.returnType();
+ Class<?> haveReturn = oldType.returnType();
+ boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
+
// Now build a chain of one or more adapters.
- MethodHandle adapter = target;
- MethodType midType = oldType.changeReturnType(newType.returnType());
+ MethodHandle adapter = target, adapter2;
+ MethodType midType = oldType;
for (int i = 0; i <= lastConv; i++) {
Class<?> src = newType.parameterType(i); // source type
Class<?> dst = midType.parameterType(i); // destination type
@@ -149,22 +163,23 @@
continue;
}
// Work the current type backward toward the desired caller type:
- if (i != lastConv) {
- midType = midType.changeParameterType(i, src);
- } else {
+ midType = midType.changeParameterType(i, src);
+ if (i == lastConv) {
// When doing the last (or only) real conversion,
// force all remaining null conversions to happen also.
- assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src)));
- midType = newType;
+ MethodType lastMidType = newType;
+ if (retConv) lastMidType = lastMidType.changeReturnType(haveReturn);
+ assert(VerifyType.isNullConversion(lastMidType, midType));
+ midType = lastMidType;
}
// Tricky case analysis follows.
// It parallels canConvertArgument() above.
if (src.isPrimitive()) {
if (dst.isPrimitive()) {
- adapter = makePrimCast(midType, adapter, i, dst);
+ adapter2 = makePrimCast(midType, adapter, i, dst);
} else {
- adapter = makeBoxArgument(midType, adapter, i, dst);
+ adapter2 = makeBoxArgument(midType, adapter, i, src);
}
} else {
if (dst.isPrimitive()) {
@@ -174,29 +189,53 @@
// conversions supported by reflect.Method.invoke.
// Those conversions require a big nest of if/then/else logic,
// which we prefer to make a user responsibility.
- adapter = makeUnboxArgument(midType, adapter, i, dst);
+ adapter2 = makeUnboxArgument(midType, adapter, i, dst, level);
} else {
// Simple reference conversion.
// Note: Do not check for a class hierarchy relation
// between src and dst. In all cases a 'null' argument
// will pass the cast conversion.
- adapter = makeCheckCast(midType, adapter, i, dst);
+ adapter2 = makeCheckCast(midType, adapter, i, dst);
}
}
- assert(adapter != null);
- assert(adapter.type() == midType);
+ assert(adapter2 != null) : Arrays.asList(src, dst, midType, adapter, i, target, newType);
+ assert(adapter2.type() == midType);
+ adapter = adapter2;
+ }
+ if (retConv) {
+ adapter2 = makeReturnConversion(adapter, haveReturn, needReturn);
+ assert(adapter2 != null);
+ adapter = adapter2;
}
if (adapter.type() != newType) {
// Only trivial conversions remain.
- adapter = makeRetypeOnly(newType, adapter);
- assert(adapter != null);
+ adapter2 = makeRetypeOnly(newType, adapter);
+ assert(adapter2 != null);
+ adapter = adapter2;
// Actually, that's because there were no non-trivial ones:
- assert(lastConv == -1);
+ assert(lastConv == -1 || retConv);
}
assert(adapter.type() == newType);
return adapter;
}
+ private static MethodHandle makeReturnConversion(MethodHandle target, Class<?> haveReturn, Class<?> needReturn) {
+ MethodHandle adjustReturn;
+ if (haveReturn == void.class) {
+ // synthesize a zero value for the given void
+ Object zero = Wrapper.forBasicType(needReturn).zero();
+ adjustReturn = MethodHandles.constant(needReturn, zero);
+ } else {
+ MethodType needConversion = MethodType.methodType(needReturn, haveReturn);
+ adjustReturn = MethodHandles.identity(needReturn).asType(needConversion);
+ }
+ if (!canCollectArguments(adjustReturn.type(), target.type(), 0, false)) {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
+ throw new InternalError("NYI");
+ }
+ return makeCollectArguments(adjustReturn, target, 0, false);
+ }
+
/**
* Create a JVM-level adapter method handle to permute the arguments
* of the given method.
@@ -224,7 +263,7 @@
if (argumentMap.length != oldType.parameterCount())
throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
if (nullPermutation) {
- MethodHandle res = makePairwiseConvert(newType, target);
+ MethodHandle res = makePairwiseConvert(newType, target, 0);
// well, that was easy
if (res == null)
throw newIllegalArgumentException("cannot convert pairwise: "+newType);
@@ -310,11 +349,25 @@
return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT;
}
+ static int extractStackMove(int convOp) {
+ int spChange = convOp >> CONV_STACK_MOVE_SHIFT;
+ return spChange / MethodHandleNatives.JVM_STACK_MOVE_UNIT;
+ }
+
+ static int extractStackMove(MethodHandle target) {
+ if (target instanceof AdapterMethodHandle) {
+ AdapterMethodHandle amh = (AdapterMethodHandle) target;
+ return extractStackMove(amh.getConversion());
+ } else {
+ return 0;
+ }
+ }
+
/** Construct an adapter conversion descriptor for a single-argument conversion. */
private static long makeConv(int convOp, int argnum, int src, int dest) {
- assert(src == (src & 0xF));
- assert(dest == (dest & 0xF));
- assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF);
+ assert(src == (src & CONV_TYPE_MASK));
+ assert(dest == (dest & CONV_TYPE_MASK));
+ assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF || convOp == OP_COLLECT_ARGS);
int stackMove = type2size(dest) - type2size(src);
return ((long) argnum << 32 |
(long) convOp << CONV_OP_SHIFT |
@@ -323,11 +376,10 @@
insertStackMove(stackMove)
);
}
- private static long makeConv(int convOp, int argnum, int stackMove) {
- assert(convOp >= OP_DUP_ARGS && convOp <= OP_SPREAD_ARGS);
+ private static long makeDupConv(int convOp, int argnum, int stackMove) {
+ // simple argument motion, requiring one slot to specify
+ assert(convOp == OP_DUP_ARGS || convOp == OP_DROP_ARGS);
byte src = 0, dest = 0;
- if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS)
- src = dest = T_OBJECT;
return ((long) argnum << 32 |
(long) convOp << CONV_OP_SHIFT |
(int) src << CONV_SRC_TYPE_SHIFT |
@@ -336,7 +388,8 @@
);
}
private static long makeSwapConv(int convOp, int srcArg, byte type, int destSlot) {
- assert(convOp >= OP_SWAP_ARGS && convOp <= OP_ROT_ARGS);
+ // more complex argument motion, requiring two slots to specify
+ assert(convOp == OP_SWAP_ARGS || convOp == OP_ROT_ARGS);
return ((long) srcArg << 32 |
(long) convOp << CONV_OP_SHIFT |
(int) type << CONV_SRC_TYPE_SHIFT |
@@ -344,6 +397,18 @@
(int) destSlot << CONV_VMINFO_SHIFT
);
}
+ private static long makeSpreadConv(int convOp, int argnum, int src, int dest, int stackMove) {
+ // spreading or collecting, at a particular slot location
+ assert(convOp == OP_SPREAD_ARGS || convOp == OP_COLLECT_ARGS || convOp == OP_FOLD_ARGS);
+ // src = spread ? T_OBJECT (for array) : common type of collected args (else void)
+ // dest = spread ? element type of array : result type of collector (can be void)
+ return ((long) argnum << 32 |
+ (long) convOp << CONV_OP_SHIFT |
+ (int) src << CONV_SRC_TYPE_SHIFT |
+ (int) dest << CONV_DEST_TYPE_SHIFT |
+ insertStackMove(stackMove)
+ );
+ }
private static long makeConv(int convOp) {
assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW);
return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero
@@ -570,14 +635,10 @@
static boolean canPrimCast(Class<?> src, Class<?> dst) {
if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
return false;
- } else if (Wrapper.forPrimitiveType(dst).isFloating()) {
- // both must be floating types
- return Wrapper.forPrimitiveType(src).isFloating();
} else {
- // both are integral, and all combinations work fine
- assert(Wrapper.forPrimitiveType(src).isIntegral() &&
- Wrapper.forPrimitiveType(dst).isIntegral());
- return true;
+ boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
+ boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
+ return !(sflt | dflt); // no float support at present
}
}
@@ -589,6 +650,29 @@
*/
static MethodHandle makePrimCast(MethodType newType, MethodHandle target,
int arg, Class<?> convType) {
+ Class<?> src = newType.parameterType(arg);
+ if (canPrimCast(src, convType))
+ return makePrimCastOnly(newType, target, arg, convType);
+ Class<?> dst = convType;
+ boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
+ boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
+ if (sflt | dflt) {
+ MethodHandle convMethod;
+ if (sflt)
+ convMethod = ((src == double.class)
+ ? ValueConversions.convertFromDouble(dst)
+ : ValueConversions.convertFromFloat(dst));
+ else
+ convMethod = ((dst == double.class)
+ ? ValueConversions.convertToDouble(src)
+ : ValueConversions.convertToFloat(src));
+ long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
+ return new AdapterMethodHandle(target, newType, conv, convMethod);
+ }
+ throw new InternalError("makePrimCast");
+ }
+ static MethodHandle makePrimCastOnly(MethodType newType, MethodHandle target,
+ int arg, Class<?> convType) {
MethodType oldType = target.type();
if (!canPrimCast(newType, oldType, arg, convType))
return null;
@@ -602,7 +686,7 @@
* The convType is the unboxed type; it can be either a primitive or wrapper.
*/
static boolean canUnboxArgument(MethodType newType, MethodType targetType,
- int arg, Class<?> convType) {
+ int arg, Class<?> convType, int level) {
if (!convOpSupported(OP_REF_TO_PRIM)) return false;
Class<?> src = newType.parameterType(arg);
Class<?> dst = targetType.parameterType(arg);
@@ -616,21 +700,31 @@
return (diff == arg+1); // arg is sole non-trivial diff
}
/** Can an primitive unboxing adapter validly convert src to dst? */
- static boolean canUnboxArgument(Class<?> src, Class<?> dst) {
- return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive());
+ static boolean canUnboxArgument(Class<?> src, Class<?> dst, int level) {
+ assert(dst.isPrimitive());
+ // if we have JVM support for boxing, we can also do complex unboxing
+ if (convOpSupported(OP_PRIM_TO_REF)) return true;
+ Wrapper dw = Wrapper.forPrimitiveType(dst);
+ // Level 0 means cast and unbox. This works on any reference.
+ if (level == 0) return !src.isPrimitive();
+ assert(level >= 0 && level <= 2);
+ // Levels 1 and 2 allow widening and/or narrowing conversions.
+ // These are not supported directly by the JVM.
+ // But if the input reference is monomorphic, we can do it.
+ return dw.wrapperType() == src;
}
/** Factory method: Unbox the given argument.
* Return null if this cannot be done.
*/
static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target,
- int arg, Class<?> convType) {
+ int arg, Class<?> convType, int level) {
MethodType oldType = target.type();
Class<?> src = newType.parameterType(arg);
Class<?> dst = oldType.parameterType(arg);
Class<?> boxType = Wrapper.asWrapperType(convType);
Class<?> primType = Wrapper.asPrimitiveType(convType);
- if (!canUnboxArgument(newType, oldType, arg, convType))
+ if (!canUnboxArgument(newType, oldType, arg, convType, level))
return null;
MethodType castDone = newType;
if (!VerifyType.isNullConversion(src, boxType))
@@ -642,19 +736,46 @@
return makeCheckCast(newType, adapter, arg, boxType);
}
+ /** Can a boxing conversion validly convert src to dst? */
+ static boolean canBoxArgument(MethodType newType, MethodType targetType,
+ int arg, Class<?> convType) {
+ if (!convOpSupported(OP_PRIM_TO_REF)) return false;
+ Class<?> src = newType.parameterType(arg);
+ Class<?> dst = targetType.parameterType(arg);
+ Class<?> boxType = Wrapper.asWrapperType(convType);
+ convType = Wrapper.asPrimitiveType(convType);
+ if (!canCheckCast(boxType, dst)
+ || boxType == convType
+ || !VerifyType.isNullConversion(src, convType))
+ return false;
+ int diff = diffTypes(newType, targetType, false);
+ return (diff == arg+1); // arg is sole non-trivial diff
+ }
+
/** Can an primitive boxing adapter validly convert src to dst? */
static boolean canBoxArgument(Class<?> src, Class<?> dst) {
if (!convOpSupported(OP_PRIM_TO_REF)) return false;
- throw new UnsupportedOperationException("NYI");
+ return (src.isPrimitive() && !dst.isPrimitive());
}
- /** Factory method: Unbox the given argument.
+ /** Factory method: Box the given argument.
* Return null if this cannot be done.
*/
static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
int arg, Class<?> convType) {
- // this is difficult to do in the JVM because it must GC
- return null;
+ MethodType oldType = target.type();
+ Class<?> src = newType.parameterType(arg);
+ Class<?> dst = oldType.parameterType(arg);
+ Class<?> boxType = Wrapper.asWrapperType(convType);
+ Class<?> primType = Wrapper.asPrimitiveType(convType);
+ if (!canBoxArgument(newType, oldType, arg, convType)) {
+ return null;
+ }
+ if (!VerifyType.isNullConversion(boxType, dst))
+ target = makeCheckCast(oldType.changeParameterType(arg, boxType), target, arg, dst);
+ MethodHandle boxerMethod = ValueConversions.box(Wrapper.forPrimitiveType(primType));
+ long conv = makeConv(OP_PRIM_TO_REF, arg, basicType(primType), T_OBJECT);
+ return new AdapterMethodHandle(target, newType, conv, boxerMethod);
}
/** Can an adapter simply drop arguments to convert the target to newType? */
@@ -699,7 +820,7 @@
int slotCount = keep1InSlot - dropSlot;
assert(slotCount >= dropArgCount);
assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount());
- long conv = makeConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
+ long conv = makeDupConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
return new AdapterMethodHandle(target, newType, conv);
}
@@ -739,7 +860,7 @@
int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
int slotCount = keep1InSlot - dupSlot;
assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount());
- long conv = makeConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
+ long conv = makeDupConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
return new AdapterMethodHandle(target, newType, conv);
}
@@ -900,7 +1021,7 @@
for (int i = 0; i < spreadArgCount; i++) {
Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
Class<?> dst = targetType.parameterType(spreadArgPos + i);
- if (src == null || !VerifyType.isNullConversion(src, dst))
+ if (src == null || !canConvertArgument(src, dst, 1))
return false;
}
return true;
@@ -910,24 +1031,100 @@
/** Factory method: Spread selected argument. */
static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target,
Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
+ // FIXME: Get rid of newType; derive new arguments from structure of spreadArgType
MethodType targetType = target.type();
if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
return null;
+ // dest is not significant; remove?
+ int dest = T_VOID;
+ for (int i = 0; i < spreadArgCount; i++) {
+ Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
+ if (arg == null) arg = Object.class;
+ int dest2 = basicType(arg);
+ if (dest == T_VOID) dest = dest2;
+ else if (dest != dest2) dest = T_VOID;
+ if (dest == T_VOID) break;
+ targetType = targetType.changeParameterType(spreadArgPos + i, arg);
+ }
+ target = target.asType(targetType);
+ int arrayArgSize = 1; // always a reference
// in arglist: [0: ...keep1 | spos: spreadArg | spos+1: keep2... ]
// out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ]
int keep2OutPos = spreadArgPos + spreadArgCount;
- int spreadSlot = targetType.parameterSlotDepth(keep2OutPos);
- int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos);
- int slotCount = keep1OutSlot - spreadSlot;
- assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+1));
+ int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); // leading edge of |spread...|
+ int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); // trailing edge of |spread...|
+ assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+arrayArgSize));
+ int slotCount = keep1OutSlot - spreadSlot; // slots in |spread...|
assert(slotCount >= spreadArgCount);
- long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, slotCount-1);
+ int stackMove = - arrayArgSize + slotCount; // pop array, push N slots
+ long conv = makeSpreadConv(OP_SPREAD_ARGS, spreadArgPos, T_OBJECT, dest, stackMove);
MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType);
assert(res.type().parameterType(spreadArgPos) == spreadArgType);
return res;
}
- // TO DO: makeCollectArguments, makeFlyby, makeRicochet
+ /** Can an adapter collect a series of arguments, replacing them by zero or one results? */
+ static boolean canCollectArguments(MethodType targetType,
+ MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) {
+ if (!convOpSupported(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS)) return false;
+ int collectArgCount = collectorType.parameterCount();
+ Class<?> rtype = collectorType.returnType();
+ assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype)
+ // [(Object)Object[], (Object[])Object[], 0, 1]
+ : Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount)
+ ;
+ return true;
+ }
+
+ /** Factory method: Collect or filter selected argument(s). */
+ static MethodHandle makeCollectArguments(MethodHandle target,
+ MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
+ assert(canCollectArguments(target.type(), collector.type(), collectArgPos, retainOriginalArgs));
+ MethodType targetType = target.type();
+ MethodType collectorType = collector.type();
+ int collectArgCount = collectorType.parameterCount();
+ Class<?> collectValType = collectorType.returnType();
+ int collectValCount = (collectValType == void.class ? 0 : 1);
+ int collectValSlots = collectorType.returnSlotCount();
+ MethodType newType = targetType
+ .dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
+ if (!retainOriginalArgs) {
+ newType = newType
+ .insertParameterTypes(collectArgPos, collectorType.parameterList());
+ } else {
+ // parameter types at the fold point must be the same
+ assert(diffParamTypes(newType, collectArgPos, targetType, collectValCount, collectArgCount, false) == 0)
+ : Arrays.asList(target, collector, collectArgPos, retainOriginalArgs);
+ }
+ // in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ]
+ // out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
+ // out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
+ int keep2InPos = collectArgPos + collectArgCount;
+ int keep1InSlot = newType.parameterSlotDepth(collectArgPos); // leading edge of |collect...|
+ int collectSlot = newType.parameterSlotDepth(keep2InPos); // trailing edge of |collect...|
+ int slotCount = keep1InSlot - collectSlot; // slots in |collect...|
+ assert(slotCount >= collectArgCount);
+ assert(collectSlot == targetType.parameterSlotDepth(
+ collectArgPos + collectValCount + (retainOriginalArgs ? collectArgCount : 0) ));
+ int dest = basicType(collectValType);
+ int src = T_VOID;
+ // src is not significant; remove?
+ for (int i = 0; i < collectArgCount; i++) {
+ int src2 = basicType(collectorType.parameterType(i));
+ if (src == T_VOID) src = src2;
+ else if (src != src2) src = T_VOID;
+ if (src == T_VOID) break;
+ }
+ int stackMove = collectValSlots; // push 0..2 results
+ if (!retainOriginalArgs) stackMove -= slotCount; // pop N arguments
+ int lastCollectArg = keep2InPos-1;
+ long conv = makeSpreadConv(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS,
+ lastCollectArg, src, dest, stackMove);
+ MethodHandle res = new AdapterMethodHandle(target, newType, conv, collector);
+ assert(res.type().parameterList().subList(collectArgPos, collectArgPos+collectArgCount)
+ .equals(collector.type().parameterList()));
+ return res;
+ }
@Override
public String toString() {
--- a/jdk/src/share/classes/java/lang/invoke/CallSite.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/CallSite.java Tue May 24 14:32:10 2011 -0700
@@ -273,9 +273,9 @@
Object binding;
info = maybeReBox(info);
if (info == null) {
- binding = bootstrapMethod.invokeGeneric(caller, name, type);
+ binding = bootstrapMethod.invoke(caller, name, type);
} else if (!info.getClass().isArray()) {
- binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
+ binding = bootstrapMethod.invoke(caller, name, type, info);
} else {
Object[] argv = (Object[]) info;
maybeReBoxElements(argv);
@@ -283,10 +283,10 @@
throw new BootstrapMethodError("too many bootstrap method arguments");
MethodType bsmType = bootstrapMethod.type();
if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
- binding = bootstrapMethod.invokeGeneric(caller, name, type, argv);
+ binding = bootstrapMethod.invoke(caller, name, type, argv);
else
binding = MethodHandles.spreadInvoker(bsmType, 3)
- .invokeGeneric(bootstrapMethod, caller, name, type, argv);
+ .invoke(bootstrapMethod, caller, name, type, argv);
}
//System.out.println("BSM for "+name+type+" => "+binding);
if (binding instanceof CallSite) {
--- a/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FilterGeneric.java Tue May 24 14:32:10 2011 -0700
@@ -61,6 +61,10 @@
return ad;
}
+ static {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
+ }
+
Adapter makeInstance(Kind kind, int pos, MethodHandle filter, MethodHandle target) {
Adapter ad = getAdapter(kind, pos);
return ad.makeInstance(ad.prototypeEntryPoint(), filter, target);
--- a/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FilterOneArgument.java Tue May 24 14:32:10 2011 -0700
@@ -67,6 +67,10 @@
this.target = target;
}
+ static {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
+ }
+
public static MethodHandle make(MethodHandle filter, MethodHandle target) {
if (filter == null) return target;
if (target == null) return filter;
--- a/jdk/src/share/classes/java/lang/invoke/FromGeneric.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/FromGeneric.java Tue May 24 14:32:10 2011 -0700
@@ -98,6 +98,10 @@
this.unboxingInvoker = computeUnboxingInvoker(targetType, internalType0);
}
+ static {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
+ }
+
/**
* The typed target will be called according to targetType.
* The adapter code will in fact see the raw result from internalType,
@@ -112,10 +116,10 @@
assert(iret == Object.class);
return ValueConversions.identity();
} else if (wrap.primitiveType() == iret) {
- return ValueConversions.box(wrap, false);
+ return ValueConversions.box(wrap);
} else {
assert(tret == double.class ? iret == long.class : iret == int.class);
- return ValueConversions.boxRaw(wrap, false);
+ return ValueConversions.boxRaw(wrap);
}
}
@@ -135,7 +139,7 @@
MethodType fixArgsType = internalType.changeReturnType(targetType.returnType());
MethodHandle fixArgs = MethodHandleImpl.convertArguments(
invoker, Invokers.invokerType(fixArgsType),
- invoker.type(), null);
+ invoker.type(), 0);
if (fixArgs == null)
throw new InternalError("bad fixArgs");
// reinterpret the calling sequence as raw:
@@ -160,7 +164,6 @@
/** Build an adapter of the given generic type, which invokes typedTarget
* on the incoming arguments, after unboxing as necessary.
* The return value is boxed if necessary.
- * @param genericType the required type of the result
* @param typedTarget the target
* @return an adapter method handle
*/
@@ -231,7 +234,7 @@
}
static Adapter buildAdapterFromBytecodes(MethodType internalType) {
- throw new UnsupportedOperationException("NYI");
+ throw new UnsupportedOperationException("NYI "+internalType);
}
/**
--- a/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/InvokeGeneric.java Tue May 24 14:32:10 2011 -0700
@@ -29,12 +29,12 @@
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
/**
- * Adapters which manage MethodHandle.invokeGeneric calls.
+ * Adapters which manage inexact MethodHandle.invoke calls.
* The JVM calls one of these when the exact type match fails.
* @author jrose
*/
class InvokeGeneric {
- // erased type for the call, which originates from an invokeGeneric site
+ // erased type for the call, which originates from an inexact invoke site
private final MethodType erasedCallerType;
// an invoker of type (MT, MH; A...) -> R
private final MethodHandle initialInvoker;
@@ -56,7 +56,7 @@
}
/** Return the adapter information for this type's erasure. */
- /*non-public*/ static MethodHandle genericInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException {
+ /*non-public*/ static MethodHandle generalInvokerOf(MethodType erasedCallerType) throws ReflectiveOperationException {
InvokeGeneric gen = new InvokeGeneric(erasedCallerType);
return gen.initialInvoker;
}
--- a/jdk/src/share/classes/java/lang/invoke/Invokers.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/Invokers.java Tue May 24 14:32:10 2011 -0700
@@ -43,10 +43,10 @@
private /*lazy*/ MethodHandle erasedInvoker;
/*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric
- // generic (untyped) invoker for the outgoing call
- private /*lazy*/ MethodHandle genericInvoker;
+ // general invoker for the outgoing call
+ private /*lazy*/ MethodHandle generalInvoker;
- // generic (untyped) invoker for the outgoing call; accepts a single Object[]
+ // general invoker for the outgoing call; accepts a single Object[]
private final /*lazy*/ MethodHandle[] spreadInvokers;
// invoker for an unbound callsite
@@ -77,13 +77,13 @@
return invoker;
}
- /*non-public*/ MethodHandle genericInvoker() {
+ /*non-public*/ MethodHandle generalInvoker() {
MethodHandle invoker1 = exactInvoker();
- MethodHandle invoker = genericInvoker;
+ MethodHandle invoker = generalInvoker;
if (invoker != null) return invoker;
- MethodType genericType = targetType.generic();
- invoker = MethodHandles.convertArguments(invoker1, invokerType(genericType));
- genericInvoker = invoker;
+ MethodType generalType = targetType.generic();
+ invoker = invoker1.asType(invokerType(generalType));
+ generalInvoker = invoker;
return invoker;
}
@@ -93,9 +93,9 @@
if (invoker != null) return invoker;
MethodType erasedType = targetType.erase();
if (erasedType == targetType.generic())
- invoker = genericInvoker();
+ invoker = generalInvoker();
else
- invoker = MethodHandles.convertArguments(invoker1, invokerType(erasedType));
+ invoker = invoker1.asType(invokerType(erasedType));
erasedInvoker = invoker;
return invoker;
}
@@ -103,7 +103,7 @@
/*non-public*/ MethodHandle spreadInvoker(int objectArgCount) {
MethodHandle vaInvoker = spreadInvokers[objectArgCount];
if (vaInvoker != null) return vaInvoker;
- MethodHandle gInvoker = genericInvoker();
+ MethodHandle gInvoker = generalInvoker();
vaInvoker = gInvoker.asSpreader(Object[].class, targetType.parameterCount() - objectArgCount);
spreadInvokers[objectArgCount] = vaInvoker;
return vaInvoker;
--- a/jdk/src/share/classes/java/lang/invoke/MemberName.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MemberName.java Tue May 24 14:32:10 2011 -0700
@@ -525,7 +525,7 @@
/** A factory type for resolving member names with the help of the VM.
* TBD: Define access-safe public constructors for this factory.
*/
- public static class Factory {
+ /*non-public*/ static class Factory {
private Factory() { } // singleton pattern
static Factory INSTANCE = new Factory();
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandle.java Tue May 24 14:32:10 2011 -0700
@@ -26,6 +26,7 @@
package java.lang.invoke;
+import sun.invoke.util.ValueConversions;
import static java.lang.invoke.MethodHandleStatics.*;
/**
@@ -53,12 +54,12 @@
* and the kinds of transformations that apply to it.
* <p>
* A method handle contains a pair of special invoker methods
- * called {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}.
+ * called {@link #invokeExact invokeExact} and {@link #invoke invoke}.
* Both invoker methods provide direct access to the method handle's
* underlying method, constructor, field, or other operation,
* as modified by transformations of arguments and return values.
* Both invokers accept calls which exactly match the method handle's own type.
- * The {@code invokeGeneric} invoker also accepts a range of other call types.
+ * The plain, inexact invoker also accepts a range of other call types.
* <p>
* Method handles are immutable and have no visible state.
* Of course, they can be bound to underlying methods or data which exhibit state.
@@ -76,7 +77,7 @@
* may change from time to time or across implementations from different vendors.
*
* <h3>Method handle compilation</h3>
- * A Java method call expression naming {@code invokeExact} or {@code invokeGeneric}
+ * A Java method call expression naming {@code invokeExact} or {@code invoke}
* can invoke a method handle from Java source code.
* From the viewpoint of source code, these methods can take any arguments
* and their result can be cast to any return type.
@@ -86,7 +87,7 @@
* which connects this freedom of invocation directly to the JVM execution stack.
* <p>
* As is usual with virtual methods, source-level calls to {@code invokeExact}
- * and {@code invokeGeneric} compile to an {@code invokevirtual} instruction.
+ * and {@code invoke} compile to an {@code invokevirtual} instruction.
* More unusually, the compiler must record the actual argument types,
* and may not perform method invocation conversions on the arguments.
* Instead, it must push them on the stack according to their own unconverted types.
@@ -109,7 +110,7 @@
* The first time a {@code invokevirtual} instruction is executed
* it is linked, by symbolically resolving the names in the instruction
* and verifying that the method call is statically legal.
- * This is true of calls to {@code invokeExact} and {@code invokeGeneric}.
+ * This is true of calls to {@code invokeExact} and {@code invoke}.
* In this case, the type descriptor emitted by the compiler is checked for
* correct syntax and names it contains are resolved.
* Thus, an {@code invokevirtual} instruction which invokes
@@ -127,18 +128,18 @@
* In the case of {@code invokeExact}, the type descriptor of the invocation
* (after resolving symbolic type names) must exactly match the method type
* of the receiving method handle.
- * In the case of {@code invokeGeneric}, the resolved type descriptor
+ * In the case of plain, inexact {@code invoke}, the resolved type descriptor
* must be a valid argument to the receiver's {@link #asType asType} method.
- * Thus, {@code invokeGeneric} is more permissive than {@code invokeExact}.
+ * Thus, plain {@code invoke} is more permissive than {@code invokeExact}.
* <p>
* After type matching, a call to {@code invokeExact} directly
* and immediately invoke the method handle's underlying method
* (or other behavior, as the case may be).
* <p>
- * A call to {@code invokeGeneric} works the same as a call to
+ * A call to plain {@code invoke} works the same as a call to
* {@code invokeExact}, if the type descriptor specified by the caller
* exactly matches the method handle's own type.
- * If there is a type mismatch, {@code invokeGeneric} attempts
+ * If there is a type mismatch, {@code invoke} attempts
* to adjust the type of the receiving method handle,
* as if by a call to {@link #asType asType},
* to obtain an exactly invokable method handle {@code M2}.
@@ -152,7 +153,7 @@
* In typical programs, method handle type matching will usually succeed.
* But if a match fails, the JVM will throw a {@link WrongMethodTypeException},
* either directly (in the case of {@code invokeExact}) or indirectly as if
- * by a failed call to {@code asType} (in the case of {@code invokeGeneric}).
+ * by a failed call to {@code asType} (in the case of {@code invoke}).
* <p>
* Thus, a method type mismatch which might show up as a linkage error
* in a statically typed program can show up as
@@ -249,8 +250,8 @@
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
-x = mh.invokeGeneric("one", "two");
-// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+x = mh.invoke("one", "two");
+// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assert(x.equals(java.util.Arrays.asList("one","two")));
// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
@@ -269,12 +270,12 @@
mh.invokeExact(System.out, "Hello, world.");
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
* </pre></blockquote>
- * Each of the above calls to {@code invokeExact} or {@code invokeGeneric}
+ * Each of the above calls to {@code invokeExact} or plain {@code invoke}
* generates a single invokevirtual instruction with
* the type descriptor indicated in the following comment.
*
* <h3>Exceptions</h3>
- * The methods {@code invokeExact} and {@code invokeGeneric} are declared
+ * The methods {@code invokeExact} and {@code invoke} are declared
* to throw {@link java.lang.Throwable Throwable},
* which is to say that there is no static restriction on what a method handle
* can throw. Since the JVM does not distinguish between checked
@@ -288,7 +289,7 @@
*
* <h3><a name="sigpoly"></a>Signature polymorphism</h3>
* The unusual compilation and linkage behavior of
- * {@code invokeExact} and {@code invokeGeneric}
+ * {@code invokeExact} and plain {@code invoke}
* is referenced by the term <em>signature polymorphism</em>.
* A signature polymorphic method is one which can operate with
* any of a wide range of call signatures and return types.
@@ -322,7 +323,7 @@
* The following methods (and no others) are signature polymorphic:
* <ul>
* <li>{@link java.lang.invoke.MethodHandle#invokeExact MethodHandle.invokeExact}
- * <li>{@link java.lang.invoke.MethodHandle#invokeGeneric MethodHandle.invokeGeneric}
+ * <li>{@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}
* </ul>
* <p>
* A signature polymorphic method will be declared with the following properties:
@@ -374,24 +375,34 @@
* <p>
* As a special case,
* when the Core Reflection API is used to view the signature polymorphic
- * methods {@code invokeExact} or {@code invokeGeneric} in this class,
- * they appear as single, non-polymorphic native methods.
- * Calls to these native methods do not result in method handle invocations.
+ * methods {@code invokeExact} or plain {@code invoke} in this class,
+ * they appear as ordinary non-polymorphic methods.
+ * Their reflective appearance, as viewed by
+ * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod},
+ * is unaffected by their special status in this API.
+ * For example, {@link java.lang.reflect.Method#getModifiers Method.getModifiers}
+ * will report exactly those modifier bits required for any similarly
+ * declared method, including in this case {@code native} and {@code varargs} bits.
+ * <p>
+ * As with any reflected method, these methods (when reflected) may be
+ * invoked via {@link java.lang.reflect.Method#invoke Method.invoke}.
+ * However, such reflective calls do not result in method handle invocations.
+ * Such a call, if passed the required argument
+ * (a single one, of type {@code Object[]}), will ignore the argument and
+ * will throw an {@code UnsupportedOperationException}.
+ * <p>
* Since {@code invokevirtual} instructions can natively
* invoke method handles under any type descriptor, this reflective view conflicts
- * with the normal presentation via bytecodes.
- * Thus, these two native methods, as viewed by
- * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod},
- * are placeholders only.
- * If invoked via {@link java.lang.reflect.Method#invoke Method.invoke},
- * they will throw {@code UnsupportedOperationException}.
+ * with the normal presentation of these methods via bytecodes.
+ * Thus, these two native methods, when reflectively viewed by
+ * {@code Class.getDeclaredMethod}, may be regarded as placeholders only.
* <p>
* In order to obtain an invoker method for a particular type descriptor,
* use {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker},
- * or {@link java.lang.invoke.MethodHandles#genericInvoker MethodHandles.genericInvoker}.
+ * or {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}.
* The {@link java.lang.invoke.MethodHandles.Lookup#findVirtual Lookup.findVirtual}
* API is also able to return a method handle
- * to call {@code invokeExact} or {@code invokeGeneric},
+ * to call {@code invokeExact} or plain {@code invoke},
* for any specified type descriptor .
*
* <h3>Interoperation between method handles and Java generics</h3>
@@ -523,7 +534,7 @@
* adaptations directly on the caller's arguments,
* and call the target method handle according to its own exact type.
* <p>
- * The type descriptor at the call site of {@code invokeGeneric} must
+ * The type descriptor at the call site of {@code invoke} must
* be a valid argument to the receivers {@code asType} method.
* In particular, the caller must specify the same argument arity
* as the callee's type,
@@ -539,11 +550,18 @@
* @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails
* @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
*/
+ public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
+
+ /**
+ * <em>Temporary alias</em> for {@link #invoke}, for backward compatibility with some versions of JSR 292.
+ * On some JVMs, support can be excluded by the flags {@code -XX:+UnlockExperimentalVMOptions -XX:-AllowInvokeGeneric}.
+ * @deprecated Will be removed for JSR 292 Proposed Final Draft.
+ */
public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable;
/**
* Performs a varargs invocation, passing the arguments in the given array
- * to the method handle, as if via {@link #invokeGeneric invokeGeneric} from a call site
+ * to the method handle, as if via an inexact {@link #invoke invoke} from a call site
* which mentions only the type {@code Object}, and whose arity is the length
* of the argument array.
* <p>
@@ -553,7 +571,7 @@
* <ul>
* <li>Determine the length of the argument array as {@code N}.
* For a null reference, {@code N=0}. </li>
- * <li>Determine the generic type {@code TN} of {@code N} arguments as
+ * <li>Determine the general type {@code TN} of {@code N} arguments as
* as {@code TN=MethodType.genericMethodType(N)}.</li>
* <li>Force the original target method handle {@code MH0} to the
* required type, as {@code MH1 = MH0.asType(TN)}. </li>
@@ -580,7 +598,7 @@
* Object result = invoker.invokeExact(this, arguments);
* </pre></blockquote>
* <p>
- * Unlike the signature polymorphic methods {@code invokeExact} and {@code invokeGeneric},
+ * Unlike the signature polymorphic methods {@code invokeExact} and {@code invoke},
* {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI.
* It can therefore be used as a bridge between native or reflective code and method handles.
*
@@ -595,11 +613,11 @@
int argc = arguments == null ? 0 : arguments.length;
MethodType type = type();
if (type.parameterCount() != argc) {
- // simulate invokeGeneric
+ // simulate invoke
return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments);
}
if (argc <= 10) {
- MethodHandle invoker = type.invokers().genericInvoker();
+ MethodHandle invoker = type.invokers().generalInvoker();
switch (argc) {
case 0: return invoker.invokeExact(this);
case 1: return invoker.invokeExact(this,
@@ -644,7 +662,7 @@
/**
* Performs a varargs invocation, passing the arguments in the given array
- * to the method handle, as if via {@link #invokeGeneric invokeGeneric} from a call site
+ * to the method handle, as if via an inexact {@link #invoke invoke} from a call site
* which mentions only the type {@code Object}, and whose arity is the length
* of the argument array.
* <p>
@@ -672,9 +690,9 @@
* If the original type and new type are equal, returns {@code this}.
* <p>
* This method provides the crucial behavioral difference between
- * {@link #invokeExact invokeExact} and {@link #invokeGeneric invokeGeneric}. The two methods
+ * {@link #invokeExact invokeExact} and plain, inexact {@link #invoke invoke}. The two methods
* perform the same steps when the caller's type descriptor is identical
- * with the callee's, but when the types differ, {@link #invokeGeneric invokeGeneric}
+ * with the callee's, but when the types differ, plain {@link #invoke invoke}
* also calls {@code asType} (or some internal equivalent) in order
* to match up the caller's and callee's types.
* <p>
@@ -689,6 +707,9 @@
* @see MethodHandles#convertArguments
*/
public MethodHandle asType(MethodType newType) {
+ if (!type.isConvertibleTo(newType)) {
+ throw new WrongMethodTypeException("cannot convert "+type+" to "+newType);
+ }
return MethodHandles.convertArguments(this, newType);
}
@@ -731,13 +752,9 @@
public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
Class<?> arrayElement = arrayType.getComponentType();
if (arrayElement == null) throw newIllegalArgumentException("not an array type");
- MethodType oldType = type();
- int nargs = oldType.parameterCount();
+ int nargs = type().parameterCount();
if (nargs < arrayLength) throw newIllegalArgumentException("bad spread array length");
- int keepPosArgs = nargs - arrayLength;
- MethodType newType = oldType.dropParameterTypes(keepPosArgs, nargs);
- newType = newType.insertParameterTypes(keepPosArgs, arrayType);
- return MethodHandles.spreadArguments(this, newType);
+ return MethodHandleImpl.spreadArguments(this, arrayType, arrayLength);
}
/**
@@ -780,15 +797,18 @@
* @see #asVarargsCollector
*/
public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
+ asCollectorChecks(arrayType, arrayLength);
+ MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength);
+ return MethodHandleImpl.collectArguments(this, type.parameterCount()-1, collector);
+ }
+
+ private void asCollectorChecks(Class<?> arrayType, int arrayLength) {
Class<?> arrayElement = arrayType.getComponentType();
- if (arrayElement == null) throw newIllegalArgumentException("not an array type");
- MethodType oldType = type();
- int nargs = oldType.parameterCount();
- if (nargs == 0) throw newIllegalArgumentException("no trailing argument");
- MethodType newType = oldType.dropParameterTypes(nargs-1, nargs);
- newType = newType.insertParameterTypes(nargs-1,
- java.util.Collections.<Class<?>>nCopies(arrayLength, arrayElement));
- return MethodHandles.collectArguments(this, newType);
+ if (arrayElement == null)
+ throw newIllegalArgumentException("not an array type", arrayType);
+ int nargs = type().parameterCount();
+ if (nargs == 0 || !type().parameterType(nargs-1).isAssignableFrom(arrayType))
+ throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
}
/**
@@ -798,7 +818,7 @@
* <p>
* The type and behavior of the adapter will be the same as
* the type and behavior of the target, except that certain
- * {@code invokeGeneric} and {@code asType} requests can lead to
+ * {@code invoke} and {@code asType} requests can lead to
* trailing positional arguments being collected into target's
* trailing parameter.
* Also, the last parameter type of the adapter will be
@@ -812,17 +832,17 @@
* since it accepts a whole array of indeterminate length,
* rather than a fixed number of arguments.)
* <p>
- * When called with {@link #invokeGeneric invokeGeneric}, if the caller
+ * When called with plain, inexact {@link #invoke invoke}, if the caller
* type is the same as the adapter, the adapter invokes the target as with
* {@code invokeExact}.
- * (This is the normal behavior for {@code invokeGeneric} when types match.)
+ * (This is the normal behavior for {@code invoke} when types match.)
* <p>
* Otherwise, if the caller and adapter arity are the same, and the
* trailing parameter type of the caller is a reference type identical to
* or assignable to the trailing parameter type of the adapter,
* the arguments and return values are converted pairwise,
* as if by {@link MethodHandles#convertArguments convertArguments}.
- * (This is also normal behavior for {@code invokeGeneric} in such a case.)
+ * (This is also normal behavior for {@code invoke} in such a case.)
* <p>
* Otherwise, the arities differ, or the adapter's trailing parameter
* type is not assignable from the corresponding caller type.
@@ -838,7 +858,7 @@
* where {@code N} is the arity of the target.
* Also, there must exist conversions from the incoming arguments
* to the target's arguments.
- * As with other uses of {@code invokeGeneric}, if these basic
+ * As with other uses of plain {@code invoke}, if these basic
* requirements are not fulfilled, a {@code WrongMethodTypeException}
* may be thrown.
* <p>
@@ -856,7 +876,7 @@
* <p>
* The behavior of {@link #asType asType} is also specialized for
* variable arity adapters, to maintain the invariant that
- * {@code invokeGeneric} is always equivalent to an {@code asType}
+ * plain, inexact {@code invoke} is always equivalent to an {@code asType}
* call to adjust the target type, followed by {@code invokeExact}.
* Therefore, a variable arity adapter responds
* to an {@code asType} request by building a fixed arity collector,
@@ -893,12 +913,12 @@
MethodHandle asList = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
.asVarargsCollector(Object[].class);
-assertEquals("[]", asList.invokeGeneric().toString());
-assertEquals("[1]", asList.invokeGeneric(1).toString());
-assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
+assertEquals("[]", asList.invoke().toString());
+assertEquals("[1]", asList.invoke(1).toString());
+assertEquals("[two, too]", asList.invoke("two", "too").toString());
Object[] argv = { "three", "thee", "tee" };
-assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
-List ls = (List) asList.invokeGeneric((Object)argv);
+assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
+List ls = (List) asList.invoke((Object)argv);
assertEquals(1, ls.size());
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
* </pre></blockquote>
@@ -926,9 +946,9 @@
.asVarargsCollector(Object[].class);
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
assert(vamh.type().equals(mh.type()));
-assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
+assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString());
boolean failed = false;
-try { mh.invokeGeneric(1,2,3); }
+try { mh.invoke(1,2,3); }
catch (WrongMethodTypeException ex) { failed = true; }
assert(failed);
* </pre></blockquote>
@@ -960,7 +980,7 @@
* <li>an {@code ldc} instruction of a {@code CONSTANT_MethodHandle}
* which resolves to a variable arity Java method or constructor
* </ul>
- * @return true if this method handle accepts more than one arity of {@code invokeGeneric} calls
+ * @return true if this method handle accepts more than one arity of plain, inexact {@code invoke} calls
* @see #asVarargsCollector
*/
public boolean isVarargsCollector() {
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Tue May 24 14:32:10 2011 -0700
@@ -121,11 +121,11 @@
if (nargs < INVOKES.length) {
MethodHandle invoke = INVOKES[nargs];
MethodType conType = CON_TYPES[nargs];
- MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, null);
+ MethodHandle gcon = convertArguments(rawConstructor, conType, rawConType, 0);
if (gcon == null) return null;
MethodHandle galloc = new AllocateObject(invoke, allocateClass, gcon);
assert(galloc.type() == newType.generic());
- return convertArguments(galloc, newType, galloc.type(), null);
+ return convertArguments(galloc, newType, galloc.type(), 0);
} else {
MethodHandle invoke = VARARGS_INVOKE;
MethodType conType = CON_TYPES[nargs];
@@ -256,8 +256,8 @@
FieldAccessor.ahandle(arrayClass, true)
};
if (mhs[0].type().parameterType(0) == Class.class) {
- mhs[0] = MethodHandles.insertArguments(mhs[0], 0, elemClass);
- mhs[1] = MethodHandles.insertArguments(mhs[1], 0, elemClass);
+ mhs[0] = mhs[0].bindTo(elemClass);
+ mhs[1] = mhs[1].bindTo(elemClass);
}
synchronized (FieldAccessor.ARRAY_CACHE) {} // memory barrier
FieldAccessor.ARRAY_CACHE.put(elemClass, mhs);
@@ -372,7 +372,7 @@
if (evclass != vclass || (!isStatic && ecclass != cclass)) {
MethodType strongType = FieldAccessor.ftype(cclass, vclass, isSetter, isStatic);
strongType = strongType.insertParameterTypes(0, FieldAccessor.class);
- mh = MethodHandles.convertArguments(mh, strongType);
+ mh = convertArguments(mh, strongType, 0);
}
return mh;
}
@@ -439,8 +439,8 @@
}
if (caclass != null) {
MethodType strongType = FieldAccessor.atype(caclass, isSetter);
- mh = MethodHandles.insertArguments(mh, 0, caclass);
- mh = MethodHandles.convertArguments(mh, strongType);
+ mh = mh.bindTo(caclass);
+ mh = convertArguments(mh, strongType, 0);
}
return mh;
}
@@ -465,7 +465,7 @@
dmh.type().parameterType(0).isAssignableFrom(receiver.getClass())) {
MethodHandle bmh = new BoundMethodHandle(dmh, receiver, 0);
MethodType newType = target.type().dropParameterTypes(0, 1);
- return convertArguments(bmh, newType, bmh.type(), null);
+ return convertArguments(bmh, newType, bmh.type(), 0);
}
}
}
@@ -486,301 +486,378 @@
return new BoundMethodHandle(target, receiver, argnum);
}
- static MethodHandle convertArguments(MethodHandle target,
+ static MethodHandle permuteArguments(MethodHandle target,
MethodType newType,
MethodType oldType,
int[] permutationOrNull) {
assert(oldType.parameterCount() == target.type().parameterCount());
- if (permutationOrNull != null) {
- int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
- if (permutationOrNull.length != outargs)
- throw newIllegalArgumentException("wrong number of arguments in permutation");
- // Make the individual outgoing argument types match up first.
- Class<?>[] callTypeArgs = new Class<?>[outargs];
- for (int i = 0; i < outargs; i++)
- callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
- MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
- target = convertArguments(target, callType, oldType, null);
- assert(target != null);
- oldType = target.type();
- List<Integer> goal = new ArrayList<Integer>(); // i*TOKEN
- List<Integer> state = new ArrayList<Integer>(); // i*TOKEN
- List<Integer> drops = new ArrayList<Integer>(); // not tokens
- List<Integer> dups = new ArrayList<Integer>(); // not tokens
- final int TOKEN = 10; // to mark items which are symbolic only
- // state represents the argument values coming into target
- for (int i = 0; i < outargs; i++) {
- state.add(permutationOrNull[i] * TOKEN);
+ int outargs = oldType.parameterCount(), inargs = newType.parameterCount();
+ if (permutationOrNull.length != outargs)
+ throw newIllegalArgumentException("wrong number of arguments in permutation");
+ // Make the individual outgoing argument types match up first.
+ Class<?>[] callTypeArgs = new Class<?>[outargs];
+ for (int i = 0; i < outargs; i++)
+ callTypeArgs[i] = newType.parameterType(permutationOrNull[i]);
+ MethodType callType = MethodType.methodType(oldType.returnType(), callTypeArgs);
+ target = convertArguments(target, callType, oldType, 0);
+ assert(target != null);
+ oldType = target.type();
+ List<Integer> goal = new ArrayList<Integer>(); // i*TOKEN
+ List<Integer> state = new ArrayList<Integer>(); // i*TOKEN
+ List<Integer> drops = new ArrayList<Integer>(); // not tokens
+ List<Integer> dups = new ArrayList<Integer>(); // not tokens
+ final int TOKEN = 10; // to mark items which are symbolic only
+ // state represents the argument values coming into target
+ for (int i = 0; i < outargs; i++) {
+ state.add(permutationOrNull[i] * TOKEN);
+ }
+ // goal represents the desired state
+ for (int i = 0; i < inargs; i++) {
+ if (state.contains(i * TOKEN)) {
+ goal.add(i * TOKEN);
+ } else {
+ // adapter must initially drop all unused arguments
+ drops.add(i);
}
- // goal represents the desired state
- for (int i = 0; i < inargs; i++) {
- if (state.contains(i * TOKEN)) {
- goal.add(i * TOKEN);
- } else {
- // adapter must initially drop all unused arguments
- drops.add(i);
+ }
+ // detect duplications
+ while (state.size() > goal.size()) {
+ for (int i2 = 0; i2 < state.size(); i2++) {
+ int arg1 = state.get(i2);
+ int i1 = state.indexOf(arg1);
+ if (i1 != i2) {
+ // found duplicate occurrence at i2
+ int arg2 = (inargs++) * TOKEN;
+ state.set(i2, arg2);
+ dups.add(goal.indexOf(arg1));
+ goal.add(arg2);
}
}
- // detect duplications
- while (state.size() > goal.size()) {
- for (int i2 = 0; i2 < state.size(); i2++) {
- int arg1 = state.get(i2);
- int i1 = state.indexOf(arg1);
- if (i1 != i2) {
- // found duplicate occurrence at i2
- int arg2 = (inargs++) * TOKEN;
- state.set(i2, arg2);
- dups.add(goal.indexOf(arg1));
- goal.add(arg2);
+ }
+ assert(state.size() == goal.size());
+ int size = goal.size();
+ while (!state.equals(goal)) {
+ // Look for a maximal sequence of adjacent misplaced arguments,
+ // and try to rotate them into place.
+ int bestRotArg = -10 * TOKEN, bestRotLen = 0;
+ int thisRotArg = -10 * TOKEN, thisRotLen = 0;
+ for (int i = 0; i < size; i++) {
+ int arg = state.get(i);
+ // Does this argument match the current run?
+ if (arg == thisRotArg + TOKEN) {
+ thisRotArg = arg;
+ thisRotLen += 1;
+ if (bestRotLen < thisRotLen) {
+ bestRotLen = thisRotLen;
+ bestRotArg = thisRotArg;
+ }
+ } else {
+ // The old sequence (if any) stops here.
+ thisRotLen = 0;
+ thisRotArg = -10 * TOKEN;
+ // But maybe a new one starts here also.
+ int wantArg = goal.get(i);
+ final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION;
+ if (arg != wantArg &&
+ arg >= wantArg - TOKEN * MAX_ARG_ROTATION &&
+ arg <= wantArg + TOKEN * MAX_ARG_ROTATION) {
+ thisRotArg = arg;
+ thisRotLen = 1;
}
}
}
- assert(state.size() == goal.size());
- int size = goal.size();
- while (!state.equals(goal)) {
- // Look for a maximal sequence of adjacent misplaced arguments,
- // and try to rotate them into place.
- int bestRotArg = -10 * TOKEN, bestRotLen = 0;
- int thisRotArg = -10 * TOKEN, thisRotLen = 0;
- for (int i = 0; i < size; i++) {
- int arg = state.get(i);
- // Does this argument match the current run?
- if (arg == thisRotArg + TOKEN) {
- thisRotArg = arg;
- thisRotLen += 1;
- if (bestRotLen < thisRotLen) {
- bestRotLen = thisRotLen;
- bestRotArg = thisRotArg;
- }
- } else {
- // The old sequence (if any) stops here.
- thisRotLen = 0;
- thisRotArg = -10 * TOKEN;
- // But maybe a new one starts here also.
- int wantArg = goal.get(i);
- final int MAX_ARG_ROTATION = AdapterMethodHandle.MAX_ARG_ROTATION;
- if (arg != wantArg &&
- arg >= wantArg - TOKEN * MAX_ARG_ROTATION &&
- arg <= wantArg + TOKEN * MAX_ARG_ROTATION) {
- thisRotArg = arg;
- thisRotLen = 1;
- }
+ if (bestRotLen >= 2) {
+ // Do a rotation if it can improve argument positioning
+ // by at least 2 arguments. This is not always optimal,
+ // but it seems to catch common cases.
+ int dstEnd = state.indexOf(bestRotArg);
+ int srcEnd = goal.indexOf(bestRotArg);
+ int rotBy = dstEnd - srcEnd;
+ int dstBeg = dstEnd - (bestRotLen - 1);
+ int srcBeg = srcEnd - (bestRotLen - 1);
+ assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs
+ // Make a span which covers both source and destination.
+ int rotBeg = Math.min(dstBeg, srcBeg);
+ int rotEnd = Math.max(dstEnd, srcEnd);
+ int score = 0;
+ for (int i = rotBeg; i <= rotEnd; i++) {
+ if ((int)state.get(i) != (int)goal.get(i))
+ score += 1;
+ }
+ List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1);
+ Collections.rotate(rotSpan, -rotBy); // reverse direction
+ for (int i = rotBeg; i <= rotEnd; i++) {
+ if ((int)state.get(i) != (int)goal.get(i))
+ score -= 1;
+ }
+ if (score >= 2) {
+ // Improved at least two argument positions. Do it.
+ List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
+ Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy);
+ MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes);
+ MethodHandle nextTarget
+ = AdapterMethodHandle.makeRotateArguments(rotType, target,
+ rotBeg, rotSpan.size(), rotBy);
+ if (nextTarget != null) {
+ //System.out.println("Rot: "+rotSpan+" by "+rotBy);
+ target = nextTarget;
+ oldType = rotType;
+ continue;
}
}
- if (bestRotLen >= 2) {
- // Do a rotation if it can improve argument positioning
- // by at least 2 arguments. This is not always optimal,
- // but it seems to catch common cases.
- int dstEnd = state.indexOf(bestRotArg);
- int srcEnd = goal.indexOf(bestRotArg);
- int rotBy = dstEnd - srcEnd;
- int dstBeg = dstEnd - (bestRotLen - 1);
- int srcBeg = srcEnd - (bestRotLen - 1);
- assert((dstEnd | dstBeg | srcEnd | srcBeg) >= 0); // no negs
- // Make a span which covers both source and destination.
- int rotBeg = Math.min(dstBeg, srcBeg);
- int rotEnd = Math.max(dstEnd, srcEnd);
- int score = 0;
- for (int i = rotBeg; i <= rotEnd; i++) {
- if ((int)state.get(i) != (int)goal.get(i))
- score += 1;
- }
- List<Integer> rotSpan = state.subList(rotBeg, rotEnd+1);
- Collections.rotate(rotSpan, -rotBy); // reverse direction
- for (int i = rotBeg; i <= rotEnd; i++) {
- if ((int)state.get(i) != (int)goal.get(i))
- score -= 1;
- }
- if (score >= 2) {
- // Improved at least two argument positions. Do it.
- List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
- Collections.rotate(ptypes.subList(rotBeg, rotEnd+1), -rotBy);
- MethodType rotType = MethodType.methodType(oldType.returnType(), ptypes);
- MethodHandle nextTarget
- = AdapterMethodHandle.makeRotateArguments(rotType, target,
- rotBeg, rotSpan.size(), rotBy);
- if (nextTarget != null) {
- //System.out.println("Rot: "+rotSpan+" by "+rotBy);
- target = nextTarget;
- oldType = rotType;
- continue;
- }
- }
- // Else de-rotate, and drop through to the swap-fest.
- Collections.rotate(rotSpan, rotBy);
- }
+ // Else de-rotate, and drop through to the swap-fest.
+ Collections.rotate(rotSpan, rotBy);
+ }
- // Now swap like the wind!
- List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
- for (int i = 0; i < size; i++) {
- // What argument do I want here?
- int arg = goal.get(i);
- if (arg != state.get(i)) {
- // Where is it now?
- int j = state.indexOf(arg);
- Collections.swap(ptypes, i, j);
- MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes);
- target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j);
- if (target == null) throw newIllegalArgumentException("cannot swap");
- assert(target.type() == swapType);
- oldType = swapType;
- Collections.swap(state, i, j);
- }
+ // Now swap like the wind!
+ List<Class<?>> ptypes = Arrays.asList(oldType.parameterArray());
+ for (int i = 0; i < size; i++) {
+ // What argument do I want here?
+ int arg = goal.get(i);
+ if (arg != state.get(i)) {
+ // Where is it now?
+ int j = state.indexOf(arg);
+ Collections.swap(ptypes, i, j);
+ MethodType swapType = MethodType.methodType(oldType.returnType(), ptypes);
+ target = AdapterMethodHandle.makeSwapArguments(swapType, target, i, j);
+ if (target == null) throw newIllegalArgumentException("cannot swap");
+ assert(target.type() == swapType);
+ oldType = swapType;
+ Collections.swap(state, i, j);
}
- // One pass of swapping must finish the job.
- assert(state.equals(goal));
+ }
+ // One pass of swapping must finish the job.
+ assert(state.equals(goal));
+ }
+ while (!dups.isEmpty()) {
+ // Grab a contiguous trailing sequence of dups.
+ int grab = dups.size() - 1;
+ int dupArgPos = dups.get(grab), dupArgCount = 1;
+ while (grab - 1 >= 0) {
+ int dup0 = dups.get(grab - 1);
+ if (dup0 != dupArgPos - 1) break;
+ dupArgPos -= 1;
+ dupArgCount += 1;
+ grab -= 1;
+ }
+ //if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size()));
+ dups.subList(grab, dups.size()).clear();
+ // In the new target type drop that many args from the tail:
+ List<Class<?>> ptypes = oldType.parameterList();
+ ptypes = ptypes.subList(0, ptypes.size() - dupArgCount);
+ MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes);
+ target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount);
+ if (target == null)
+ throw newIllegalArgumentException("cannot dup");
+ oldType = target.type();
+ }
+ while (!drops.isEmpty()) {
+ // Grab a contiguous initial sequence of drops.
+ int dropArgPos = drops.get(0), dropArgCount = 1;
+ while (dropArgCount < drops.size()) {
+ int drop1 = drops.get(dropArgCount);
+ if (drop1 != dropArgPos + dropArgCount) break;
+ dropArgCount += 1;
}
- while (!dups.isEmpty()) {
- // Grab a contiguous trailing sequence of dups.
- int grab = dups.size() - 1;
- int dupArgPos = dups.get(grab), dupArgCount = 1;
- while (grab - 1 >= 0) {
- int dup0 = dups.get(grab - 1);
- if (dup0 != dupArgPos - 1) break;
- dupArgPos -= 1;
- dupArgCount += 1;
- grab -= 1;
- }
- //if (dupArgCount > 1) System.out.println("Dup: "+dups.subList(grab, dups.size()));
- dups.subList(grab, dups.size()).clear();
- // In the new target type drop that many args from the tail:
- List<Class<?>> ptypes = oldType.parameterList();
- ptypes = ptypes.subList(0, ptypes.size() - dupArgCount);
- MethodType dupType = MethodType.methodType(oldType.returnType(), ptypes);
- target = AdapterMethodHandle.makeDupArguments(dupType, target, dupArgPos, dupArgCount);
- if (target == null)
- throw newIllegalArgumentException("cannot dup");
- oldType = target.type();
+ //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount));
+ drops.subList(0, dropArgCount).clear();
+ List<Class<?>> dropTypes = newType.parameterList()
+ .subList(dropArgPos, dropArgPos + dropArgCount);
+ MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes);
+ target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount);
+ if (target == null) throw newIllegalArgumentException("cannot drop");
+ oldType = target.type();
+ }
+ return convertArguments(target, newType, oldType, 0);
+ }
+
+ /*non-public*/ static
+ MethodHandle convertArguments(MethodHandle target, MethodType newType, int level) {
+ MethodType oldType = target.type();
+ if (oldType.equals(newType))
+ return target;
+ assert(level > 1 || oldType.isConvertibleTo(newType));
+ MethodHandle retFilter = null;
+ Class<?> oldRT = oldType.returnType();
+ Class<?> newRT = newType.returnType();
+ if (!VerifyType.isNullConversion(oldRT, newRT)) {
+ if (oldRT == void.class) {
+ Wrapper wrap = newRT.isPrimitive() ? Wrapper.forPrimitiveType(newRT) : Wrapper.OBJECT;
+ retFilter = ValueConversions.zeroConstantFunction(wrap);
+ } else {
+ retFilter = MethodHandles.identity(newRT);
+ retFilter = convertArguments(retFilter, retFilter.type().changeParameterType(0, oldRT), level);
}
- while (!drops.isEmpty()) {
- // Grab a contiguous initial sequence of drops.
- int dropArgPos = drops.get(0), dropArgCount = 1;
- while (dropArgCount < drops.size()) {
- int drop1 = drops.get(dropArgCount);
- if (drop1 != dropArgPos + dropArgCount) break;
- dropArgCount += 1;
- }
- //if (dropArgCount > 1) System.out.println("Drop: "+drops.subList(0, dropArgCount));
- drops.subList(0, dropArgCount).clear();
- List<Class<?>> dropTypes = newType.parameterList()
- .subList(dropArgPos, dropArgPos + dropArgCount);
- MethodType dropType = oldType.insertParameterTypes(dropArgPos, dropTypes);
- target = AdapterMethodHandle.makeDropArguments(dropType, target, dropArgPos, dropArgCount);
- if (target == null) throw newIllegalArgumentException("cannot drop");
- oldType = target.type();
- }
+ newType = newType.changeReturnType(oldRT);
+ }
+ MethodHandle res = null;
+ Exception ex = null;
+ try {
+ res = convertArguments(target, newType, oldType, level);
+ } catch (IllegalArgumentException ex1) {
+ ex = ex1;
}
+ if (res == null) {
+ WrongMethodTypeException wmt = new WrongMethodTypeException("cannot convert to "+newType+": "+target);
+ wmt.initCause(ex);
+ throw wmt;
+ }
+ if (retFilter != null)
+ res = MethodHandles.filterReturnValue(res, retFilter);
+ return res;
+ }
+
+ static MethodHandle convertArguments(MethodHandle target,
+ MethodType newType,
+ MethodType oldType,
+ int level) {
+ assert(oldType.parameterCount() == target.type().parameterCount());
if (newType == oldType)
return target;
if (oldType.parameterCount() != newType.parameterCount())
- throw newIllegalArgumentException("mismatched parameter count");
- MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target);
+ throw newIllegalArgumentException("mismatched parameter count", oldType, newType);
+ MethodHandle res = AdapterMethodHandle.makePairwiseConvert(newType, target, level);
if (res != null)
return res;
+ // We can come here in the case of target(int)void => (Object)void,
+ // because the unboxing logic for Object => int is complex.
int argc = oldType.parameterCount();
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
// The JVM can't do it directly, so fill in the gap with a Java adapter.
// TO DO: figure out what to put here from case-by-case experience
// Use a heavier method: Convert all the arguments to Object,
// then back to the desired types. We might have to use Java-based
// method handles to do this.
MethodType objType = MethodType.genericMethodType(argc);
- MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target);
+ MethodHandle objTarget = AdapterMethodHandle.makePairwiseConvert(objType, target, level);
if (objTarget == null)
objTarget = FromGeneric.make(target);
- res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget);
+ res = AdapterMethodHandle.makePairwiseConvert(newType, objTarget, level);
if (res != null)
return res;
return ToGeneric.make(newType, objTarget);
}
+ static MethodHandle spreadArguments(MethodHandle target, Class<?> arrayType, int arrayLength) {
+ MethodType oldType = target.type();
+ int nargs = oldType.parameterCount();
+ int keepPosArgs = nargs - arrayLength;
+ MethodType newType = oldType
+ .dropParameterTypes(keepPosArgs, nargs)
+ .insertParameterTypes(keepPosArgs, arrayType);
+ return spreadArguments(target, newType, keepPosArgs, arrayType, arrayLength);
+ }
+ static MethodHandle spreadArguments(MethodHandle target, MethodType newType, int spreadArgPos) {
+ int arrayLength = target.type().parameterCount() - spreadArgPos;
+ return spreadArguments(target, newType, spreadArgPos, Object[].class, arrayLength);
+ }
static MethodHandle spreadArguments(MethodHandle target,
MethodType newType,
- int spreadArg) {
+ int spreadArgPos,
+ Class<?> arrayType,
+ int arrayLength) {
// TO DO: maybe allow the restarg to be Object and implicitly cast to Object[]
MethodType oldType = target.type();
// spread the last argument of newType to oldType
- int spreadCount = oldType.parameterCount() - spreadArg;
- Class<Object[]> spreadArgType = Object[].class;
- MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, spreadArgType, spreadArg, spreadCount);
- if (res != null)
- return res;
- // try an intermediate adapter
- Class<?> spreadType = null;
- if (spreadArg < 0 || spreadArg >= newType.parameterCount()
- || !VerifyType.isSpreadArgType(spreadType = newType.parameterType(spreadArg)))
- throw newIllegalArgumentException("no restarg in "+newType);
- Class<?>[] ptypes = oldType.parameterArray();
- for (int i = 0; i < spreadCount; i++)
- ptypes[spreadArg + i] = VerifyType.spreadArgElementType(spreadType, i);
- MethodType midType = MethodType.methodType(newType.returnType(), ptypes);
- // after spreading, some arguments may need further conversion
- MethodHandle target2 = convertArguments(target, midType, oldType, null);
- if (target2 == null)
- throw new UnsupportedOperationException("NYI: convert "+midType+" =calls=> "+oldType);
- res = AdapterMethodHandle.makeSpreadArguments(newType, target2, spreadArgType, spreadArg, spreadCount);
- if (res != null)
- return res;
- res = SpreadGeneric.make(target2, spreadCount);
- if (res != null)
- res = convertArguments(res, newType, res.type(), null);
+ assert(arrayLength == oldType.parameterCount() - spreadArgPos);
+ assert(newType.parameterType(spreadArgPos) == arrayType);
+ MethodHandle res = AdapterMethodHandle.makeSpreadArguments(newType, target, arrayType, spreadArgPos, arrayLength);
+ if (res == null) throw new IllegalArgumentException("spread on "+target+" with "+arrayType.getSimpleName());
return res;
}
static MethodHandle collectArguments(MethodHandle target,
+ int collectArg,
+ MethodHandle collector) {
+ MethodType type = target.type();
+ Class<?> collectType = collector.type().returnType();
+ if (collectType != type.parameterType(collectArg))
+ target = target.asType(type.changeParameterType(collectArg, collectType));
+ MethodType newType = type
+ .dropParameterTypes(collectArg, collectArg+1)
+ .insertParameterTypes(collectArg, collector.type().parameterArray());
+ return collectArguments(target, newType, collectArg, collector);
+ }
+ static MethodHandle collectArguments(MethodHandle target,
MethodType newType,
int collectArg,
MethodHandle collector) {
MethodType oldType = target.type(); // (a...,c)=>r
- if (collector == null) {
- int numCollect = newType.parameterCount() - oldType.parameterCount() + 1;
- collector = ValueConversions.varargsArray(numCollect);
- }
// newType // (a..., b...)=>r
MethodType colType = collector.type(); // (b...)=>c
// oldType // (a..., b...)=>r
assert(newType.parameterCount() == collectArg + colType.parameterCount());
assert(oldType.parameterCount() == collectArg + 1);
- MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null);
- MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, null);
- if (gtarget == null || gcollector == null) return null;
- MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget);
- MethodHandle result = convertArguments(gresult, newType, gresult.type(), null);
+ MethodHandle result = null;
+ if (AdapterMethodHandle.canCollectArguments(oldType, colType, collectArg, false)) {
+ result = AdapterMethodHandle.makeCollectArguments(target, collector, collectArg, false);
+ }
+ if (result == null) {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
+ MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0);
+ MethodHandle gcollector = convertArguments(collector, colType.generic(), colType, 0);
+ if (gtarget == null || gcollector == null) return null;
+ MethodHandle gresult = FilterGeneric.makeArgumentCollector(gcollector, gtarget);
+ result = convertArguments(gresult, newType, gresult.type(), 0);
+ }
return result;
}
static MethodHandle filterArgument(MethodHandle target,
- int pos,
- MethodHandle filter) {
- MethodType ttype = target.type(), gttype = ttype.generic();
+ int pos,
+ MethodHandle filter) {
+ MethodType ttype = target.type();
+ MethodType ftype = filter.type();
+ assert(ftype.parameterCount() == 1);
+ MethodType rtype = ttype.changeParameterType(pos, ftype.parameterType(0));
+ MethodType gttype = ttype.generic();
if (ttype != gttype) {
- target = convertArguments(target, gttype, ttype, null);
+ target = convertArguments(target, gttype, ttype, 0);
ttype = gttype;
}
- MethodType ftype = filter.type(), gftype = ftype.generic();
- if (ftype.parameterCount() != 1)
- throw new InternalError();
+ MethodType gftype = ftype.generic();
if (ftype != gftype) {
- filter = convertArguments(filter, gftype, ftype, null);
+ filter = convertArguments(filter, gftype, ftype, 0);
ftype = gftype;
}
- if (ftype == ttype) {
+ MethodHandle result = null;
+ if (AdapterMethodHandle.canCollectArguments(ttype, ftype, pos, false)) {
+ result = AdapterMethodHandle.makeCollectArguments(target, filter, pos, false);
+ }
+ if (result == null) {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
+ if (ftype == ttype) {
// simple unary case
- return FilterOneArgument.make(filter, target);
+ result = FilterOneArgument.make(filter, target);
+ } else {
+ result = FilterGeneric.makeArgumentFilter(pos, filter, target);
+ }
}
- return FilterGeneric.makeArgumentFilter(pos, filter, target);
+ if (result.type() != rtype)
+ result = result.asType(rtype);
+ return result;
}
static MethodHandle foldArguments(MethodHandle target,
- MethodType newType,
- MethodHandle combiner) {
+ MethodType newType,
+ int foldPos,
+ MethodHandle combiner) {
MethodType oldType = target.type();
MethodType ctype = combiner.type();
- MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, null);
- MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, null);
+ if (AdapterMethodHandle.canCollectArguments(oldType, ctype, foldPos, true)) {
+ MethodHandle res = AdapterMethodHandle.makeCollectArguments(target, combiner, foldPos, true);
+ if (res != null) return res;
+ }
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
+ if (foldPos != 0) return null;
+ MethodHandle gtarget = convertArguments(target, oldType.generic(), oldType, 0);
+ MethodHandle gcombiner = convertArguments(combiner, ctype.generic(), ctype, 0);
+ if (ctype.returnType() == void.class) {
+ gtarget = dropArguments(gtarget, oldType.generic().insertParameterTypes(foldPos, Object.class), foldPos);
+ }
if (gtarget == null || gcombiner == null) return null;
MethodHandle gresult = FilterGeneric.makeArgumentFolder(gcombiner, gtarget);
- MethodHandle result = convertArguments(gresult, newType, gresult.type(), null);
- return result;
+ return convertArguments(gresult, newType, gresult.type(), 0);
}
static
@@ -802,6 +879,7 @@
this.target = target;
this.fallback = fallback;
}
+ // FIXME: Build the control flow out of foldArguments.
static MethodHandle make(MethodHandle test, MethodHandle target, MethodHandle fallback) {
MethodType type = target.type();
int nargs = type.parameterCount();
@@ -809,12 +887,12 @@
MethodHandle invoke = INVOKES[nargs];
MethodType gtype = type.generic();
assert(invoke.type().dropParameterTypes(0,1) == gtype);
- MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), null);
- MethodHandle gtarget = convertArguments(target, gtype, type, null);
- MethodHandle gfallback = convertArguments(fallback, gtype, type, null);
+ MethodHandle gtest = convertArguments(test, gtype.changeReturnType(boolean.class), test.type(), 0);
+ MethodHandle gtarget = convertArguments(target, gtype, type, 0);
+ MethodHandle gfallback = convertArguments(fallback, gtype, type, 0);
if (gtest == null || gtarget == null || gfallback == null) return null;
MethodHandle gguard = new GuardWithTest(invoke, gtest, gtarget, gfallback);
- return convertArguments(gguard, type, gtype, null);
+ return convertArguments(gguard, type, gtype, 0);
} else {
MethodHandle invoke = VARARGS_INVOKE;
MethodType gtype = MethodType.genericMethodType(1);
@@ -925,8 +1003,9 @@
GuardWithCatch(MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
this(INVOKES[target.type().parameterCount()], target, exType, catcher);
}
- GuardWithCatch(MethodHandle invoker,
- MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
+ // FIXME: Build the control flow out of foldArguments.
+ GuardWithCatch(MethodHandle invoker,
+ MethodHandle target, Class<? extends Throwable> exType, MethodHandle catcher) {
super(invoker);
this.target = target;
this.exType = exType;
@@ -1057,11 +1136,11 @@
if (nargs < GuardWithCatch.INVOKES.length) {
MethodType gtype = type.generic();
MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
- MethodHandle gtarget = convertArguments(target, gtype, type, null);
- MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, null);
+ MethodHandle gtarget = convertArguments(target, gtype, type, 0);
+ MethodHandle gcatcher = convertArguments(catcher, gcatchType, ctype, 0);
MethodHandle gguard = new GuardWithCatch(gtarget, exType, gcatcher);
if (gtarget == null || gcatcher == null || gguard == null) return null;
- return convertArguments(gguard, type, gtype, null);
+ return convertArguments(gguard, type, gtype, 0);
} else {
MethodType gtype = MethodType.genericMethodType(0, true);
MethodType gcatchType = gtype.insertParameterTypes(0, Throwable.class);
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Tue May 24 14:32:10 2011 -0700
@@ -115,6 +115,8 @@
/** Which conv-ops are implemented by the JVM? */
static final int CONV_OP_IMPLEMENTED_MASK;
+ /** Derived mode flag. Only false on some old JVM implementations. */
+ static final boolean HAVE_RICOCHET_FRAMES;
private static native void registerNatives();
static {
@@ -141,6 +143,7 @@
if (CONV_OP_IMPLEMENTED_MASK_ == 0)
CONV_OP_IMPLEMENTED_MASK_ = DEFAULT_CONV_OP_IMPLEMENTED_MASK;
CONV_OP_IMPLEMENTED_MASK = CONV_OP_IMPLEMENTED_MASK_;
+ HAVE_RICOCHET_FRAMES = (CONV_OP_IMPLEMENTED_MASK & (1<<OP_COLLECT_ARGS)) != 0;
}
// All compile-time constants go here.
@@ -186,25 +189,26 @@
*/
static final int
OP_RETYPE_ONLY = 0x0, // no argument changes; straight retype
- OP_RETYPE_RAW = 0x1, // no argument changes; straight retype
+ OP_RETYPE_RAW = 0x1, // straight retype, trusted (void->int, Object->T)
OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument
OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive
- OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI)
+ OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper
OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg)
OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
OP_DROP_ARGS = 0x9, // remove one or more argument slots
- OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI)
+ OP_COLLECT_ARGS = 0xA, // combine arguments using an auxiliary function
OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
- OP_FLYBY = 0xC, // operate first on reified argument list (NYI)
- OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI)
+ OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result
+ //OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists
CONV_OP_LIMIT = 0xE; // limit of CONV_OP enumeration
/** Shift and mask values for decoding the AMH.conversion field.
* These numbers are shared with the JVM for creating AMHs.
*/
static final int
CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
+ CONV_TYPE_MASK = 0x0F, // fits T_ADDRESS and below
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
@@ -244,8 +248,9 @@
T_LONG = 11,
T_OBJECT = 12,
//T_ARRAY = 13
- T_VOID = 14;
+ T_VOID = 14,
//T_ADDRESS = 15
+ T_ILLEGAL = 99;
/**
* Constant pool reference-kind codes, as used by CONSTANT_MethodHandle CP entries.
@@ -273,16 +278,29 @@
try {
Field con = Constants.class.getDeclaredField(name);
int jval = con.getInt(null);
- if (jval != vmval)
- throw new InternalError(name+": JVM has "+vmval+" while Java has "+jval);
+ if (jval == vmval) continue;
+ String err = (name+": JVM has "+vmval+" while Java has "+jval);
+ if (name.equals("CONV_OP_LIMIT")) {
+ System.err.println("warning: "+err);
+ continue;
+ }
+ throw new InternalError(err);
} catch (Exception ex) {
+ if (ex instanceof NoSuchFieldException) {
+ String err = (name+": JVM has "+vmval+" which Java does not define");
+ // ignore exotic ops the JVM cares about; we just wont issue them
+ if (name.startsWith("OP_") || name.startsWith("GC_")) {
+ System.err.println("warning: "+err);
+ continue;
+ }
+ }
throw new InternalError(name+": access failed, got "+ex);
}
}
return true;
}
static {
- verifyConstants();
+ assert(verifyConstants());
}
// Up-calls from the JVM.
@@ -313,7 +331,7 @@
}
/**
- * The JVM wants to use a MethodType with invokeGeneric. Give the runtime fair warning.
+ * The JVM wants to use a MethodType with inexact invoke. Give the runtime fair warning.
*/
static void notifyGenericMethodType(MethodType type) {
type.form().notifyGenericMethodType();
@@ -323,15 +341,39 @@
* The JVM wants to raise an exception. Here's the path.
*/
static void raiseException(int code, Object actual, Object required) {
- String message;
+ String message = null;
+ switch (code) {
+ case 190: // arraylength
+ try {
+ String reqLength = "";
+ if (required instanceof AdapterMethodHandle) {
+ int conv = ((AdapterMethodHandle)required).getConversion();
+ int spChange = AdapterMethodHandle.extractStackMove(conv);
+ reqLength = " of length "+(spChange+1);
+ }
+ int actualLength = actual == null ? 0 : java.lang.reflect.Array.getLength(actual);
+ message = "required array"+reqLength+", but encountered wrong length "+actualLength;
+ break;
+ } catch (IllegalArgumentException ex) {
+ }
+ required = Object[].class; // should have been an array
+ code = 192; // checkcast
+ break;
+ }
// disregard the identity of the actual object, if it is not a class:
- if (!(actual instanceof Class) && !(actual instanceof MethodType))
- actual = actual.getClass();
- if (actual != null)
- message = "required "+required+" but encountered "+actual;
- else
- message = "required "+required;
+ if (message == null) {
+ if (!(actual instanceof Class) && !(actual instanceof MethodType))
+ actual = actual.getClass();
+ if (actual != null)
+ message = "required "+required+" but encountered "+actual;
+ else
+ message = "required "+required;
+ }
switch (code) {
+ case 190: // arraylength
+ throw new ArrayIndexOutOfBoundsException(message);
+ case 50: //_aaload
+ throw new ClassCastException(message);
case 192: // checkcast
throw new ClassCastException(message);
default:
@@ -365,4 +407,13 @@
throw err;
}
}
+
+ /**
+ * This assertion marks code which was written before ricochet frames were implemented.
+ * Such code will go away when the ports catch up.
+ */
+ static boolean workaroundWithoutRicochetFrames() {
+ assert(!HAVE_RICOCHET_FRAMES) : "this code should not be executed if `-XX:+UseRicochetFrames is enabled";
+ return true;
+ }
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java Tue May 24 14:32:10 2011 -0700
@@ -63,8 +63,17 @@
}
static void checkSpreadArgument(Object av, int n) {
- if (av == null ? n != 0 : ((Object[])av).length != n)
- throw newIllegalArgumentException("Array is not of length "+n);
+ if (av == null) {
+ if (n == 0) return;
+ } else if (av instanceof Object[]) {
+ int len = ((Object[])av).length;
+ if (len == n) return;
+ } else {
+ int len = java.lang.reflect.Array.getLength(av);
+ if (len == n) return;
+ }
+ // fall through to error:
+ throw newIllegalArgumentException("Array is not of length "+n);
}
// handy shared exception makers (they simplify the common case code)
@@ -80,6 +89,9 @@
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj) {
return new IllegalArgumentException(message(message, obj));
}
+ /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
+ return new IllegalArgumentException(message(message, obj, obj2));
+ }
/*non-public*/ static Error uncaughtException(Exception ex) {
Error err = new InternalError("uncaught exception");
err.initCause(ex);
@@ -89,4 +101,8 @@
if (obj != null) message = message + ": " + obj;
return message;
}
+ private static String message(String message, Object obj, Object obj2) {
+ if (obj != null || obj2 != null) message = message + ": " + obj + ", " + obj2;
+ return message;
+ }
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Tue May 24 14:32:10 2011 -0700
@@ -190,7 +190,7 @@
* is not symbolically accessible from the lookup class's loader,
* the lookup can still succeed.
* For example, lookups for {@code MethodHandle.invokeExact} and
- * {@code MethodHandle.invokeGeneric} will always succeed, regardless of requested type.
+ * {@code MethodHandle.invoke} will always succeed, regardless of requested type.
* <li>If there is a security manager installed, it can forbid the lookup
* on various grounds (<a href="#secmgr">see below</a>).
* By contrast, the {@code ldc} instruction is not subject to
@@ -590,10 +590,10 @@
* Because of the general equivalence between {@code invokevirtual}
* instructions and method handles produced by {@code findVirtual},
* if the class is {@code MethodHandle} and the name string is
- * {@code invokeExact} or {@code invokeGeneric}, the resulting
+ * {@code invokeExact} or {@code invoke}, the resulting
* method handle is equivalent to one produced by
* {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker} or
- * {@link java.lang.invoke.MethodHandles#genericInvoker MethodHandles.genericInvoker}
+ * {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}
* with the same {@code type} argument.
*
* @param refc the class or interface from which the method is accessed
@@ -1080,7 +1080,7 @@
MethodType rawType = mh.type();
if (rawType.parameterType(0) == caller) return mh;
MethodType narrowType = rawType.changeParameterType(0, caller);
- MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, null);
+ MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, 0);
return fixVarargs(narrowMH, mh);
}
@@ -1148,7 +1148,7 @@
* <li>an {@code Object[]} array containing more arguments
* </ul>
* <p>
- * The invoker will behave like a call to {@link MethodHandle#invokeGeneric invokeGeneric} with
+ * The invoker will behave like a call to {@link MethodHandle#invoke invoke} with
* the indicated {@code type}.
* That is, if the target is exactly of the given {@code type}, it will behave
* like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType}
@@ -1166,7 +1166,7 @@
* <p>
* This method is equivalent to the following code (though it may be more efficient):
* <p><blockquote><pre>
-MethodHandle invoker = MethodHandles.genericInvoker(type);
+MethodHandle invoker = MethodHandles.invoker(type);
int spreadArgCount = type.parameterCount - objectArgCount;
invoker = invoker.asSpreader(Object[].class, spreadArgCount);
return invoker;
@@ -1186,7 +1186,7 @@
/**
* Produces a special <em>invoker method handle</em> which can be used to
- * invoke any method handle of the given type, as if by {@code invokeExact}.
+ * invoke any method handle of the given type, as if by {@link MethodHandle#invokeExact invokeExact}.
* The resulting invoker will have a type which is
* exactly equal to the desired type, except that it will accept
* an additional leading argument of type {@code MethodHandle}.
@@ -1203,7 +1203,7 @@
* For example, to emulate an {@code invokeExact} call to a variable method
* handle {@code M}, extract its type {@code T},
* look up the invoker method {@code X} for {@code T},
- * and call the invoker method, as {@code X.invokeGeneric(T, A...)}.
+ * and call the invoker method, as {@code X.invoke(T, A...)}.
* (It would not work to call {@code X.invokeExact}, since the type {@code T}
* is unknown.)
* If spreading, collecting, or other argument transformations are required,
@@ -1212,7 +1212,7 @@
* <p>
* <em>(Note: The invoker method is not available via the Core Reflection API.
* An attempt to call {@linkplain java.lang.reflect.Method#invoke Method.invoke}
- * on the declared {@code invokeExact} or {@code invokeGeneric} method will raise an
+ * on the declared {@code invokeExact} or {@code invoke} method will raise an
* {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
* <p>
* This method throws no reflective or security exceptions.
@@ -1226,20 +1226,20 @@
/**
* Produces a special <em>invoker method handle</em> which can be used to
- * invoke any method handle of the given type, as if by {@code invokeGeneric}.
+ * invoke any method handle compatible with the given type, as if by {@link MethodHandle#invoke invoke}.
* The resulting invoker will have a type which is
* exactly equal to the desired type, except that it will accept
* an additional leading argument of type {@code MethodHandle}.
* <p>
* Before invoking its target, the invoker will apply reference casts as
- * necessary and unbox and widen primitive arguments, as if by {@link #convertArguments convertArguments}.
- * The return value of the invoker will be an {@code Object} reference,
- * boxing a primitive value if the original type returns a primitive,
- * and always null if the original type returns void.
+ * necessary and box, unbox, or widen primitive values, as if by {@link MethodHandle#asType asType}.
+ * Similarly, the return value will be converted as necessary.
+ * If the target is a {@linkplain MethodHandle#asVarargsCollector variable arity method handle},
+ * the required arity conversion will be made, again as if by {@link MethodHandle#asType asType}.
* <p>
* This method is equivalent to the following code (though it may be more efficient):
* <p><blockquote><pre>
-publicLookup().findVirtual(MethodHandle.class, "invokeGeneric", type)
+publicLookup().findVirtual(MethodHandle.class, "invoke", type)
* </pre></blockquote>
* <p>
* This method throws no reflective or security exceptions.
@@ -1247,8 +1247,17 @@
* @return a method handle suitable for invoking any method handle convertible to the given type
*/
static public
+ MethodHandle invoker(MethodType type) {
+ return type.invokers().generalInvoker();
+ }
+
+ /**
+ * <em>Temporary alias</em> for {@link #invoker}, for backward compatibility with some versions of JSR 292.
+ * @deprecated Will be removed for JSR 292 Proposed Final Draft.
+ */
+ public static
MethodHandle genericInvoker(MethodType type) {
- return type.invokers().genericInvoker();
+ return invoker(type);
}
/**
@@ -1368,18 +1377,7 @@
*/
public static
MethodHandle convertArguments(MethodHandle target, MethodType newType) {
- MethodType oldType = target.type();
- if (oldType.equals(newType))
- return target;
- MethodHandle res = null;
- try {
- res = MethodHandleImpl.convertArguments(target,
- newType, oldType, null);
- } catch (IllegalArgumentException ex) {
- }
- if (res == null)
- throw new WrongMethodTypeException("cannot convert to "+newType+": "+target);
- return res;
+ return MethodHandleImpl.convertArguments(target, newType, 1);
}
/**
@@ -1422,7 +1420,7 @@
*/
public static
MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
- return convertArguments(target, newType); // FIXME!
+ return MethodHandleImpl.convertArguments(target, newType, 2);
}
/*
@@ -1517,23 +1515,32 @@
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
MethodType oldType = target.type();
checkReorder(reorder, newType, oldType);
- return MethodHandleImpl.convertArguments(target,
+ return MethodHandleImpl.permuteArguments(target,
newType, oldType,
reorder);
}
private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) {
+ if (newType.returnType() != oldType.returnType())
+ throw newIllegalArgumentException("return types do not match",
+ oldType, newType);
if (reorder.length == oldType.parameterCount()) {
int limit = newType.parameterCount();
boolean bad = false;
- for (int i : reorder) {
+ for (int j = 0; j < reorder.length; j++) {
+ int i = reorder[j];
if (i < 0 || i >= limit) {
bad = true; break;
}
+ Class<?> src = newType.parameterType(i);
+ Class<?> dst = oldType.parameterType(j);
+ if (src != dst)
+ throw newIllegalArgumentException("parameter types do not match after reorder",
+ oldType, newType);
}
if (!bad) return;
}
- throw newIllegalArgumentException("bad reorder array");
+ throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
}
/**
@@ -1622,7 +1629,7 @@
if (type == void.class)
throw newIllegalArgumentException("void type");
Wrapper w = Wrapper.forPrimitiveType(type);
- return identity(type).bindTo(w.convert(value, type));
+ return insertArguments(identity(type), 0, w.convert(value, type));
} else {
return identity(type).bindTo(type.cast(value));
}
@@ -1857,7 +1864,8 @@
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
MethodType targetType = target.type();
MethodHandle adapter = target;
- MethodType adapterType = targetType;
+ MethodType adapterType = null;
+ assert((adapterType = targetType) != null);
int maxPos = targetType.parameterCount();
if (pos + filters.length > maxPos)
throw newIllegalArgumentException("too many filters");
@@ -1865,19 +1873,23 @@
for (MethodHandle filter : filters) {
curPos += 1;
if (filter == null) continue; // ignore null elements of filters
- MethodType filterType = filter.type();
- if (filterType.parameterCount() != 1
- || filterType.returnType() != targetType.parameterType(curPos))
- throw newIllegalArgumentException("target and filter types do not match");
- adapterType = adapterType.changeParameterType(curPos, filterType.parameterType(0));
- adapter = MethodHandleImpl.filterArgument(adapter, curPos, filter);
+ adapter = filterArgument(adapter, curPos, filter);
+ assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null);
}
- MethodType midType = adapter.type();
- if (midType != adapterType)
- adapter = MethodHandleImpl.convertArguments(adapter, adapterType, midType, null);
+ assert(adapterType.equals(adapter.type()));
return adapter;
}
+ /*non-public*/ static
+ MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) {
+ MethodType targetType = target.type();
+ MethodType filterType = filter.type();
+ if (filterType.parameterCount() != 1
+ || filterType.returnType() != targetType.parameterType(pos))
+ throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
+ return MethodHandleImpl.filterArgument(target, pos, filter);
+ }
+
/**
* Adapts a target method handle {@code target} by post-processing
* its return value with a unary filter function.
@@ -1913,14 +1925,26 @@
MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
MethodType targetType = target.type();
MethodType filterType = filter.type();
- if (filterType.parameterCount() != 1
- || filterType.parameterType(0) != targetType.returnType())
- throw newIllegalArgumentException("target and filter types do not match");
+ Class<?> rtype = targetType.returnType();
+ int filterValues = filterType.parameterCount();
+ if (filterValues == 0
+ ? (rtype != void.class)
+ : (rtype != filterType.parameterType(0)))
+ throw newIllegalArgumentException("target and filter types do not match", target, filter);
// result = fold( lambda(retval, arg...) { filter(retval) },
// lambda( arg...) { target(arg...) } )
+ MethodType newType = targetType.changeReturnType(filterType.returnType());
+ MethodHandle result = null;
+ if (AdapterMethodHandle.canCollectArguments(filterType, targetType, 0, false)) {
+ result = AdapterMethodHandle.makeCollectArguments(filter, target, 0, false);
+ if (result != null) return result;
+ }
// FIXME: Too many nodes here.
- MethodHandle returner = dropArguments(filter, 1, targetType.parameterList());
- return foldArguments(returner, target);
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
+ MethodHandle returner = dropArguments(filter, filterValues, targetType.parameterList());
+ result = foldArguments(returner, target);
+ assert(result.type().equals(newType));
+ return result;
}
/**
@@ -1972,16 +1996,23 @@
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
MethodType targetType = target.type();
MethodType combinerType = combiner.type();
+ int foldPos = 0; // always at the head, at present
int foldArgs = combinerType.parameterCount();
- boolean ok = (targetType.parameterCount() >= 1 + foldArgs);
- if (ok && !combinerType.parameterList().equals(targetType.parameterList().subList(1, foldArgs+1)))
+ int foldVals = combinerType.returnType() == void.class ? 0 : 1;
+ int afterInsertPos = foldPos + foldVals;
+ boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
+ if (ok && !(combinerType.parameterList()
+ .equals(targetType.parameterList().subList(afterInsertPos,
+ afterInsertPos + foldArgs))))
ok = false;
- if (ok && !combinerType.returnType().equals(targetType.parameterType(0)))
+ if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0)))
ok = false;
if (!ok)
throw misMatchedTypes("target and combiner types", targetType, combinerType);
- MethodType newType = targetType.dropParameterTypes(0, 1);
- return MethodHandleImpl.foldArguments(target, newType, combiner);
+ MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos);
+ MethodHandle res = MethodHandleImpl.foldArguments(target, newType, foldPos, combiner);
+ if (res == null) throw newIllegalArgumentException("cannot fold from "+newType+" to " +targetType);
+ return res;
}
/**
@@ -2142,7 +2173,7 @@
* the given {@code target} on the incoming arguments,
* and returning or throwing whatever the {@code target}
* returns or throws. The invocation will be as if by
- * {@code target.invokeGeneric}.
+ * {@code target.invoke}.
* The target's type will be checked before the
* instance is created, as if by a call to {@code asType},
* which may result in a {@code WrongMethodTypeException}.
--- a/jdk/src/share/classes/java/lang/invoke/MethodType.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java Tue May 24 14:32:10 2011 -0700
@@ -25,6 +25,7 @@
package java.lang.invoke;
+import sun.invoke.util.Wrapper;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -39,7 +40,7 @@
* matched between a method handle and all its callers,
* and the JVM's operations enforce this matching at, specifically
* during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact}
- * and {@link MethodHandle#invokeGeneric MethodHandle.invokeGeneric}, and during execution
+ * and {@link MethodHandle#invoke MethodHandle.invoke}, and during execution
* of {@code invokedynamic} instructions.
* <p>
* The structure is a return type accompanied by any number of parameter types.
@@ -262,18 +263,18 @@
* Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All parameters and the return type will be {@code Object},
- * except the final varargs parameter if any, which will be {@code Object[]}.
- * @param objectArgCount number of parameters (excluding the varargs parameter if any)
- * @param varargs whether there will be a varargs parameter, of type {@code Object[]}
- * @return a totally generic method type, given only its count of parameters and varargs
- * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
+ * except the final array parameter if any, which will be {@code Object[]}.
+ * @param objectArgCount number of parameters (excluding the final array parameter if any)
+ * @param finalArray whether there will be a trailing array parameter, of type {@code Object[]}
+ * @return a generally applicable method type, for all calls of the given fixed argument count and a collected array of further arguments
+ * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray})
* @see #genericMethodType(int)
*/
public static
- MethodType genericMethodType(int objectArgCount, boolean varargs) {
+ MethodType genericMethodType(int objectArgCount, boolean finalArray) {
MethodType mt;
checkSlotCount(objectArgCount);
- int ivarargs = (!varargs ? 0 : 1);
+ int ivarargs = (!finalArray ? 0 : 1);
int ootIndex = objectArgCount*2 + ivarargs;
if (ootIndex < objectOnlyTypes.length) {
mt = objectOnlyTypes[ootIndex];
@@ -294,7 +295,7 @@
* Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
* All parameters and the return type will be Object.
* @param objectArgCount number of parameters
- * @return a totally generic method type, given only its count of parameters
+ * @return a generally applicable method type, for all calls of the given argument count
* @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
* @see #genericMethodType(int, boolean)
*/
@@ -626,6 +627,30 @@
return sb.toString();
}
+
+ /*non-public*/
+ boolean isConvertibleTo(MethodType newType) {
+ if (!canConvert(returnType(), newType.returnType()))
+ return false;
+ int argc = parameterCount();
+ if (argc != newType.parameterCount())
+ return false;
+ for (int i = 0; i < argc; i++) {
+ if (!canConvert(newType.parameterType(i), parameterType(i)))
+ return false;
+ }
+ return true;
+ }
+ private static boolean canConvert(Class<?> src, Class<?> dst) {
+ if (src == dst || dst == void.class) return true;
+ if (src.isPrimitive() && dst.isPrimitive()) {
+ if (!Wrapper.forPrimitiveType(dst)
+ .isConvertibleFrom(Wrapper.forPrimitiveType(src)))
+ return false;
+ }
+ return true;
+ }
+
/// Queries which have to do with the bytecode architecture
/** Reports the number of JVM stack slots required to invoke a method
--- a/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodTypeForm.java Tue May 24 14:32:10 2011 -0700
@@ -46,6 +46,7 @@
final long argCounts; // packed slot & value counts
final long primCounts; // packed prim & double counts
final int vmslots; // total number of parameter slots
+ private Object vmlayout; // vm-specific information for calls
final MethodType erasedType; // the canonical erasure
/*lazy*/ MethodType primsAsBoxes; // replace prims by wrappers
@@ -59,7 +60,7 @@
/*lazy*/ FromGeneric fromGeneric; // convert cs. w/o prims to with
/*lazy*/ SpreadGeneric[] spreadGeneric; // expand one argument to many
/*lazy*/ FilterGeneric filterGeneric; // convert argument(s) on the fly
- /*lazy*/ MethodHandle genericInvoker; // hook for invokeGeneric
+ /*lazy*/ MethodHandle genericInvoker; // hook for inexact invoke
public MethodType erasedType() {
return erasedType;
@@ -460,9 +461,9 @@
if (genericInvoker != null) return;
try {
// Trigger adapter creation.
- genericInvoker = InvokeGeneric.genericInvokerOf(erasedType);
+ genericInvoker = InvokeGeneric.generalInvokerOf(erasedType);
} catch (Exception ex) {
- Error err = new InternalError("Exception while resolving invokeGeneric");
+ Error err = new InternalError("Exception while resolving inexact invoke");
err.initCause(ex);
throw err;
}
--- a/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/SpreadGeneric.java Tue May 24 14:32:10 2011 -0700
@@ -66,6 +66,10 @@
this.entryPoint = ep[0];
}
+ static {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
+ }
+
/** From targetType remove the last spreadCount arguments, and instead
* append a simple Object argument.
*/
--- a/jdk/src/share/classes/java/lang/invoke/ToGeneric.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/ToGeneric.java Tue May 24 14:32:10 2011 -0700
@@ -96,7 +96,7 @@
ToGeneric va2 = ToGeneric.of(primsAtEnd);
this.adapter = va2.adapter;
if (true) throw new UnsupportedOperationException("NYI: primitive parameters must follow references; entryType = "+entryType);
- this.entryPoint = MethodHandleImpl.convertArguments(
+ this.entryPoint = MethodHandleImpl.permuteArguments(
va2.entryPoint, primsAtEnd, entryType, primsAtEndOrder);
// example: for entryType of (int,Object,Object), the reordered
// type is (Object,Object,int) and the order is {1,2,0},
@@ -128,7 +128,7 @@
assert(eptWithInts.parameterType(i) == int.class);
MethodType nextType = midType.changeParameterType(i, int.class);
rawEntryPoint = MethodHandleImpl.convertArguments(
- rawEntryPoint, nextType, midType, null);
+ rawEntryPoint, nextType, midType, 0);
midType = nextType;
}
}
@@ -152,6 +152,10 @@
this.invoker = makeRawArgumentFilter(invoker0, rawEntryTypeInit, entryType);
}
+ static {
+ assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this class is deprecated
+ }
+
/** A generic argument list will be created by a call of type 'raw'.
* The values need to be reboxed for to match 'cooked'.
* Do this on the fly.
@@ -171,7 +175,7 @@
invoker.type().generic(), invoker, 0, MethodHandle.class);
if (filteredInvoker == null) throw new UnsupportedOperationException("NYI");
}
- MethodHandle reboxer = ValueConversions.rebox(dst, false);
+ MethodHandle reboxer = ValueConversions.rebox(dst);
filteredInvoker = FilterGeneric.makeArgumentFilter(1+i, reboxer, filteredInvoker);
if (filteredInvoker == null) throw new InternalError();
}
@@ -199,13 +203,13 @@
assert(!rret.isPrimitive());
if (rret == Object.class && !mustCast)
return null;
- return ValueConversions.cast(tret, false);
+ return ValueConversions.cast(tret);
} else if (tret == rret) {
- return ValueConversions.unbox(tret, false);
+ return ValueConversions.unbox(tret);
} else {
assert(rret.isPrimitive());
assert(tret == double.class ? rret == long.class : rret == int.class);
- return ValueConversions.unboxRaw(tret, false);
+ return ValueConversions.unboxRaw(tret);
}
}
@@ -311,7 +315,7 @@
}
static Adapter buildAdapterFromBytecodes(MethodType entryPointType) {
- throw new UnsupportedOperationException("NYI");
+ throw new UnsupportedOperationException("NYI: "+entryPointType);
}
/**
--- a/jdk/src/share/classes/java/lang/invoke/package-info.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/package-info.java Tue May 24 14:32:10 2011 -0700
@@ -185,7 +185,7 @@
* The method handle constant produced for such a method behaves as if
* it were created by {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector}.
* In other words, the constant method handle will exhibit variable arity,
- * when invoked via {@code invokeGeneric}.
+ * when invoked via {@code MethodHandle.invoke}.
* On the other hand, its behavior with respect to {@code invokeExact} will be the same
* as if the {@code varargs} bit were not set.
* <p>
@@ -243,7 +243,7 @@
* <li>optionally, one or more <a href="#args">additional static arguments</a> </li>
* </ul>
* The method handle is then applied to the other values as if by
- * {@link java.lang.invoke.MethodHandle#invokeGeneric invokeGeneric}.
+ * {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke}.
* The returned result must be a {@link java.lang.invoke.CallSite CallSite} (or a subclass).
* The type of the call site's target must be exactly equal to the type
* derived from the dynamic call site's type descriptor and passed to
@@ -251,7 +251,7 @@
* The call site then becomes permanently linked to the dynamic call site.
* <p>
* As long as each bootstrap method can be correctly invoked
- * by <code>invokeGeneric</code>, its detailed type is arbitrary.
+ * by <code>MethodHandle.invoke</code>, its detailed type is arbitrary.
* For example, the first argument could be {@code Object}
* instead of {@code MethodHandles.Lookup}, and the return type
* could also be {@code Object} instead of {@code CallSite}.
@@ -272,7 +272,7 @@
* (i.e., a {@code CONSTANT_Class}, {@code CONSTANT_MethodType},
* or {@code CONSTANT_MethodHandle} argument cannot be linked) </li>
* <li>the bootstrap method has the wrong arity,
- * causing {@code invokeGeneric} to throw {@code WrongMethodTypeException} </li>
+ * causing {@code MethodHandle.invoke} to throw {@code WrongMethodTypeException} </li>
* <li>the bootstrap method has a wrong argument or return type </li>
* <li>the bootstrap method invocation completes abnormally </li>
* <li>the result from the bootstrap invocation is not a reference to
@@ -381,10 +381,10 @@
* those values will be passed as additional arguments to the method handle.
* (Note that because there is a limit of 255 arguments to any method,
* at most 252 extra arguments can be supplied.)
- * The bootstrap method will be invoked as if by either {@code invokeGeneric}
+ * The bootstrap method will be invoked as if by either {@code MethodHandle.invoke}
* or {@code invokeWithArguments}. (There is no way to tell the difference.)
* <p>
- * The normal argument conversion rules for {@code invokeGeneric} apply to all stacked arguments.
+ * The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments.
* For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
* If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
* then some or all of the arguments specified here may be collected into a trailing array parameter.
@@ -419,8 +419,8 @@
* For example, the fourth argument could be {@code MethodHandle},
* if that is the type of the corresponding constant in
* the {@code CONSTANT_InvokeDynamic} entry.
- * In that case, the {@code invokeGeneric} call will pass the extra method handle
- * constant as an {@code Object}, but the type matching machinery of {@code invokeGeneric}
+ * In that case, the {@code MethodHandle.invoke} call will pass the extra method handle
+ * constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke}
* will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
* resulting in a {@code BootstrapMethodError}.)
--- a/jdk/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html Tue May 24 14:32:10 2011 -0700
@@ -211,6 +211,20 @@
<p>
+<a name=optcolor><b>Optional ColorSpace support:</b></a>
+Handling of PhotoYCC (YCC), PhotoYCCA (YCCA), RGBA and YCbCrA color spaces
+by the standard plugin, as described below, is dependent on capabilities
+of the libraries used to interpret the JPEG data. Thus all consequential
+behaviors are optional. If the support is not available when decoding,
+the color space will be treated as unrecognized and the appropriate
+default color space for the specified number of component channels
+may be used.
+When writing, an Exception may be thrown if no suitable conversion
+can be applied before encoding.
+But where the support for these color spaces is available, the behavior
+must be as documented.
+<p>
+
When reading, the contents of the stream are interpreted by the usual
JPEG conventions, as follows:
@@ -241,8 +255,11 @@
2-channel images are assumed to be grayscale with an alpha channel.
For 3- and 4-channel images, the component ids are consulted. If these
values are 1-3 for a 3-channel image, then the image is assumed to be
- YCbCr. If these values are 1-4 for a 4-channel image, then the image
- is assumed to be YCbCrA. If these values are > 4, they are checked
+ YCbCr. Subject to the availability of the
+ <a href=#optcolor>optional color space support</a>
+ described above, if these values are 1-4 for a 4-channel image,
+ then the image is assumed to be YCbCrA.
+ If these values are > 4, they are checked
against the ASCII codes for 'R', 'G', 'B', 'A', 'C', 'c'. These can
encode the following colorspaces:
<p>
@@ -346,12 +363,16 @@
component ids in the frame and scan headers are set to 1, 2, and 3.
- <li> RGBA images are converted to YCbCrA, subsampled in the
+ <li> Subject to the <a href=#optcolor>optional library support</a>
+ described above,
+ RGBA images are converted to YCbCrA, subsampled in the
chrominance channels by half both vertically and horizontally, and
written without any special marker segments. The component ids
in the frame and scan headers are set to 1, 2, 3, and 4.
- <li> PhotoYCC and YCCAimages are subsampled by half in the chrominance
+ <li> Subject to the <a href=#optcolor>optional library support</a>
+ described above,
+ PhotoYCC and YCCAimages are subsampled by half in the chrominance
channels both vertically and horizontally and written with an
Adobe <code>APP14</code> marker segment and 'Y','C', and 'c' (and
'A' if an alpha channel is present) as component ids in the frame
@@ -433,6 +454,8 @@
</ul>
<li> RGBA images:
+ Subject to the <a href=#optcolor>optional library support</a>
+ described above,
<ul>
<li> If an <code>app0JFIF</code> node is present in the metadata object,
it is ignored and a warning is sent to listeners, as JFIF does not
@@ -456,6 +479,8 @@
</ul>
<li> PhotoYCC Images:
+ Subject to the <a href=#optcolor>optional library support</a>
+ described above,
<ul>
<li> If an <code>app0JFIF</code> node is present in the metadata object,
the image is converted to sRGB, and then to YCbCr during encoding,
@@ -471,6 +496,8 @@
</ul>
<li> PhotoYCCA Images:
+ Subject to the <a href=#optcolor>optional library support</a>
+ described above,
<ul>
<li> If an <code>app0JFIF</code> node is present in the metadata object,
it is ignored and a warning is sent to listeners, as JFIF does not
--- a/jdk/src/share/classes/javax/swing/ComboBoxModel.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/javax/swing/ComboBoxModel.java Tue May 24 14:32:10 2011 -0700
@@ -33,9 +33,11 @@
* <code>ListModel</code>. This disjoint behavior allows for the temporary
* storage and retrieval of a selected item in the model.
*
+ * @param <E> the type of the elements of this model
+ *
* @author Arnaud Weber
*/
-public interface ComboBoxModel extends ListModel {
+public interface ComboBoxModel<E> extends ListModel<E> {
/**
* Set the selected item. The implementation of this method should notify
--- a/jdk/src/share/classes/javax/swing/DefaultComboBoxModel.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/javax/swing/DefaultComboBoxModel.java Tue May 24 14:32:10 2011 -0700
@@ -24,39 +24,28 @@
*/
package javax.swing;
-import java.beans.*;
import java.util.*;
-import java.awt.*;
-import java.awt.event.*;
-
import java.io.Serializable;
-import java.io.ObjectOutputStream;
-import java.io.ObjectInputStream;
-import java.io.IOException;
-
-import javax.swing.event.*;
-import javax.swing.plaf.*;
-import javax.swing.border.*;
-
-import javax.accessibility.*;
/**
* The default model for combo boxes.
*
+ * @param <E> the type of the elements of this model
+ *
* @author Arnaud Weber
* @author Tom Santos
*/
-public class DefaultComboBoxModel extends AbstractListModel implements MutableComboBoxModel, Serializable {
- Vector objects;
+public class DefaultComboBoxModel<E> extends AbstractListModel<E> implements MutableComboBoxModel<E>, Serializable {
+ Vector<E> objects;
Object selectedObject;
/**
* Constructs an empty DefaultComboBoxModel object.
*/
public DefaultComboBoxModel() {
- objects = new Vector();
+ objects = new Vector<E>();
}
/**
@@ -65,8 +54,8 @@
*
* @param items an array of Object objects
*/
- public DefaultComboBoxModel(final Object items[]) {
- objects = new Vector();
+ public DefaultComboBoxModel(final E items[]) {
+ objects = new Vector<E>();
objects.ensureCapacity( items.length );
int i,c;
@@ -84,7 +73,7 @@
*
* @param v a Vector object ...
*/
- public DefaultComboBoxModel(Vector<?> v) {
+ public DefaultComboBoxModel(Vector<E> v) {
objects = v;
if ( getSize() > 0 ) {
@@ -117,7 +106,7 @@
}
// implements javax.swing.ListModel
- public Object getElementAt(int index) {
+ public E getElementAt(int index) {
if ( index >= 0 && index < objects.size() )
return objects.elementAt(index);
else
@@ -136,7 +125,7 @@
}
// implements javax.swing.MutableComboBoxModel
- public void addElement(Object anObject) {
+ public void addElement(E anObject) {
objects.addElement(anObject);
fireIntervalAdded(this,objects.size()-1, objects.size()-1);
if ( objects.size() == 1 && selectedObject == null && anObject != null ) {
@@ -145,7 +134,7 @@
}
// implements javax.swing.MutableComboBoxModel
- public void insertElementAt(Object anObject,int index) {
+ public void insertElementAt(E anObject,int index) {
objects.insertElementAt(anObject,index);
fireIntervalAdded(this, index, index);
}
--- a/jdk/src/share/classes/javax/swing/JComboBox.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/javax/swing/JComboBox.java Tue May 24 14:32:10 2011 -0700
@@ -69,6 +69,8 @@
* @see ComboBoxModel
* @see DefaultComboBoxModel
*
+ * @param <E> the type of the elements of this combo box
+ *
* @beaninfo
* attribute: isContainer false
* description: A combination of a text field and a drop-down list.
@@ -76,7 +78,7 @@
* @author Arnaud Weber
* @author Mark Davidson
*/
-public class JComboBox extends JComponent
+public class JComboBox<E> extends JComponent
implements ItemSelectable,ListDataListener,ActionListener, Accessible {
/**
* @see #getUIClassID
@@ -91,7 +93,7 @@
* @see #getModel
* @see #setModel
*/
- protected ComboBoxModel dataModel;
+ protected ComboBoxModel<E> dataModel;
/**
* This protected field is implementation specific. Do not access directly
* or override. Use the accessor methods instead.
@@ -99,7 +101,7 @@
* @see #getRenderer
* @see #setRenderer
*/
- protected ListCellRenderer renderer;
+ protected ListCellRenderer<? super E> renderer;
/**
* This protected field is implementation specific. Do not access directly
* or override. Use the accessor methods instead.
@@ -156,7 +158,7 @@
*/
protected Object selectedItemReminder = null;
- private Object prototypeDisplayValue;
+ private E prototypeDisplayValue;
// Flag to ensure that infinite loops do not occur with ActionEvents.
private boolean firingActionEvent = false;
@@ -175,7 +177,7 @@
* displayed list of items
* @see DefaultComboBoxModel
*/
- public JComboBox(ComboBoxModel aModel) {
+ public JComboBox(ComboBoxModel<E> aModel) {
super();
setModel(aModel);
init();
@@ -189,9 +191,9 @@
* @param items an array of objects to insert into the combo box
* @see DefaultComboBoxModel
*/
- public JComboBox(final Object items[]) {
+ public JComboBox(E[] items) {
super();
- setModel(new DefaultComboBoxModel(items));
+ setModel(new DefaultComboBoxModel<E>(items));
init();
}
@@ -203,9 +205,9 @@
* @param items an array of vectors to insert into the combo box
* @see DefaultComboBoxModel
*/
- public JComboBox(Vector<?> items) {
+ public JComboBox(Vector<E> items) {
super();
- setModel(new DefaultComboBoxModel(items));
+ setModel(new DefaultComboBoxModel<E>(items));
init();
}
@@ -219,7 +221,7 @@
*/
public JComboBox() {
super();
- setModel(new DefaultComboBoxModel());
+ setModel(new DefaultComboBoxModel<E>());
init();
}
@@ -263,7 +265,7 @@
public void updateUI() {
setUI((ComboBoxUI)UIManager.getUI(this));
- ListCellRenderer renderer = getRenderer();
+ ListCellRenderer<? super E> renderer = getRenderer();
if (renderer instanceof Component) {
SwingUtilities.updateComponentTreeUI((Component)renderer);
}
@@ -302,8 +304,8 @@
* bound: true
* description: Model that the combo box uses to get data to display.
*/
- public void setModel(ComboBoxModel aModel) {
- ComboBoxModel oldModel = dataModel;
+ public void setModel(ComboBoxModel<E> aModel) {
+ ComboBoxModel<E> oldModel = dataModel;
if (oldModel != null) {
oldModel.removeListDataListener(this);
}
@@ -322,7 +324,7 @@
* @return the <code>ComboBoxModel</code> that provides the displayed
* list of items
*/
- public ComboBoxModel getModel() {
+ public ComboBoxModel<E> getModel() {
return dataModel;
}
@@ -461,8 +463,8 @@
* expert: true
* description: The renderer that paints the item selected in the list.
*/
- public void setRenderer(ListCellRenderer aRenderer) {
- ListCellRenderer oldRenderer = renderer;
+ public void setRenderer(ListCellRenderer<? super E> aRenderer) {
+ ListCellRenderer<? super E> oldRenderer = renderer;
renderer = aRenderer;
firePropertyChange( "renderer", oldRenderer, renderer );
invalidate();
@@ -475,7 +477,7 @@
* @return the <code>ListCellRenderer</code> that displays
* the selected item.
*/
- public ListCellRenderer getRenderer() {
+ public ListCellRenderer<? super E> getRenderer() {
return renderer;
}
@@ -558,7 +560,7 @@
// will be rejected.
boolean found = false;
for (int i = 0; i < dataModel.getSize(); i++) {
- Object element = dataModel.getElementAt(i);
+ E element = dataModel.getElementAt(i);
if (anObject.equals(element)) {
found = true;
objectToSelect = element;
@@ -640,7 +642,7 @@
public int getSelectedIndex() {
Object sObject = dataModel.getSelectedItem();
int i,c;
- Object obj;
+ E obj;
for ( i=0,c=dataModel.getSize();i<c;i++ ) {
obj = dataModel.getElementAt(i);
@@ -658,7 +660,7 @@
* @see #setPrototypeDisplayValue
* @since 1.4
*/
- public Object getPrototypeDisplayValue() {
+ public E getPrototypeDisplayValue() {
return prototypeDisplayValue;
}
@@ -683,7 +685,7 @@
* attribute: visualUpdate true
* description: The display prototype value, used to compute display width and height.
*/
- public void setPrototypeDisplayValue(Object prototypeDisplayValue) {
+ public void setPrototypeDisplayValue(E prototypeDisplayValue) {
Object oldValue = this.prototypeDisplayValue;
this.prototypeDisplayValue = prototypeDisplayValue;
firePropertyChange("prototypeDisplayValue", oldValue, prototypeDisplayValue);
@@ -708,12 +710,12 @@
* }
* </pre>
*
- * @param anObject the Object to add to the list
+ * @param item the item to add to the list
* @see MutableComboBoxModel
*/
- public void addItem(Object anObject) {
+ public void addItem(E item) {
checkMutableComboBoxModel();
- ((MutableComboBoxModel)dataModel).addElement(anObject);
+ ((MutableComboBoxModel<E>)dataModel).addElement(item);
}
/**
@@ -721,14 +723,14 @@
* This method works only if the <code>JComboBox</code> uses a
* mutable data model.
*
- * @param anObject the <code>Object</code> to add to the list
+ * @param item the item to add to the list
* @param index an integer specifying the position at which
* to add the item
* @see MutableComboBoxModel
*/
- public void insertItemAt(Object anObject, int index) {
+ public void insertItemAt(E item, int index) {
checkMutableComboBoxModel();
- ((MutableComboBoxModel)dataModel).insertElementAt(anObject,index);
+ ((MutableComboBoxModel<E>)dataModel).insertElementAt(item,index);
}
/**
@@ -756,7 +758,7 @@
*/
public void removeItemAt(int anIndex) {
checkMutableComboBoxModel();
- ((MutableComboBoxModel)dataModel).removeElementAt( anIndex );
+ ((MutableComboBoxModel<E>)dataModel).removeElementAt( anIndex );
}
/**
@@ -764,7 +766,7 @@
*/
public void removeAllItems() {
checkMutableComboBoxModel();
- MutableComboBoxModel model = (MutableComboBoxModel)dataModel;
+ MutableComboBoxModel<E> model = (MutableComboBoxModel<E>)dataModel;
int size = model.getSize();
if ( model instanceof DefaultComboBoxModel ) {
@@ -772,7 +774,7 @@
}
else {
for ( int i = 0; i < size; ++i ) {
- Object element = model.getElementAt( 0 );
+ E element = model.getElementAt( 0 );
model.removeElement( element );
}
}
@@ -1188,11 +1190,11 @@
private static class ComboBoxActionPropertyChangeListener
- extends ActionPropertyChangeListener<JComboBox> {
- ComboBoxActionPropertyChangeListener(JComboBox b, Action a) {
+ extends ActionPropertyChangeListener<JComboBox<?>> {
+ ComboBoxActionPropertyChangeListener(JComboBox<?> b, Action a) {
super(b, a);
}
- protected void actionPropertyChanged(JComboBox cb,
+ protected void actionPropertyChanged(JComboBox<?> cb,
Action action,
PropertyChangeEvent e) {
if (AbstractAction.shouldReconfigure(e)) {
@@ -1454,10 +1456,10 @@
*
* @param index an integer indicating the list position, where the first
* item starts at zero
- * @return the <code>Object</code> at that list position; or
+ * @return the item at that list position; or
* <code>null</code> if out of range
*/
- public Object getItemAt(int index) {
+ public E getItemAt(int index) {
return dataModel.getElementAt(index);
}
--- a/jdk/src/share/classes/javax/swing/MutableComboBoxModel.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/javax/swing/MutableComboBoxModel.java Tue May 24 14:32:10 2011 -0700
@@ -27,19 +27,21 @@
/**
* A mutable version of <code>ComboBoxModel</code>.
*
+ * @param <E> the type of the elements of this model
+ *
* @author Tom Santos
*/
-public interface MutableComboBoxModel extends ComboBoxModel {
+public interface MutableComboBoxModel<E> extends ComboBoxModel<E> {
/**
* Adds an item at the end of the model. The implementation of this method
* should notify all registered <code>ListDataListener</code>s that the
* item has been added.
*
- * @param obj the <code>Object</code> to be added
+ * @param item the item to be added
*/
- public void addElement( Object obj );
+ public void addElement( E item );
/**
* Removes an item from the model. The implementation of this method should
@@ -55,17 +57,17 @@
* should notify all registered <code>ListDataListener</code>s that the
* item has been added.
*
- * @param obj the <code>Object</code> to be added
+ * @param item the item to be added
* @param index location to add the object
*/
- public void insertElementAt( Object obj, int index );
+ public void insertElementAt( E item, int index );
/**
* Removes an item at a specific index. The implementation of this method
* should notify all registered <code>ListDataListener</code>s that the
* item has been removed.
*
- * @param index location of object to be removed
+ * @param index location of the item to be removed
*/
public void removeElementAt( int index );
}
--- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicDirectoryModel.java Tue May 24 14:32:10 2011 -0700
@@ -40,7 +40,7 @@
*
* @author Jeff Dinkins
*/
-public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener {
+public class BasicDirectoryModel extends AbstractListModel<Object> implements PropertyChangeListener {
private JFileChooser filechooser = null;
// PENDING(jeff) pick the size more sensibly
--- a/jdk/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/javax/swing/plaf/metal/MetalFileChooserUI.java Tue May 24 14:32:10 2011 -0700
@@ -906,7 +906,7 @@
/**
* Data model for a type-face selection combo-box.
*/
- protected class DirectoryComboBoxModel extends AbstractListModel implements ComboBoxModel {
+ protected class DirectoryComboBoxModel extends AbstractListModel<Object> implements ComboBoxModel<Object> {
Vector<File> directories = new Vector<File>();
int[] depths = null;
File selectedDirectory = null;
@@ -1063,7 +1063,7 @@
/**
* Data model for a type-face selection combo-box.
*/
- protected class FilterComboBoxModel extends AbstractListModel implements ComboBoxModel, PropertyChangeListener {
+ protected class FilterComboBoxModel extends AbstractListModel<Object> implements ComboBoxModel<Object>, PropertyChangeListener {
protected FileFilter[] filters;
protected FilterComboBoxModel() {
super();
--- a/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/javax/swing/plaf/synth/SynthTabbedPaneUI.java Tue May 24 14:32:10 2011 -0700
@@ -488,6 +488,18 @@
paintContentBorder(tabContentContext, g, tabPlacement, selectedIndex);
}
+ protected void paintTabArea(Graphics g, int tabPlacement,
+ int selectedIndex) {
+ // This can be invoked from ScrollabeTabPanel
+ Insets insets = tabPane.getInsets();
+ int x = insets.left;
+ int y = insets.top;
+ int width = tabPane.getWidth() - insets.left - insets.right;
+ int height = tabPane.getHeight() - insets.top - insets.bottom;
+
+ paintTabArea(tabAreaContext, g, tabPlacement, selectedIndex,
+ new Rectangle(x, y, width, height));
+ }
private void paintTabArea(SynthContext ss, Graphics g,
int tabPlacement, int selectedIndex,
--- a/jdk/src/share/classes/sun/invoke/util/ValueConversions.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/ValueConversions.java Tue May 24 14:32:10 2011 -0700
@@ -31,10 +31,15 @@
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
public class ValueConversions {
+ private static final Class<?> THIS_CLASS = ValueConversions.class;
+ // Do not adjust this except for special platforms:
+ private static final int MAX_ARITY = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255);
+
private static final Lookup IMPL_LOOKUP = MethodHandles.lookup();
private static EnumMap<Wrapper, MethodHandle>[] newWrapperCaches(int n) {
@@ -42,88 +47,101 @@
EnumMap<Wrapper, MethodHandle>[] caches
= (EnumMap<Wrapper, MethodHandle>[]) new EnumMap[n]; // unchecked warning expected here
for (int i = 0; i < n; i++)
- caches[i] = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
+ caches[i] = new EnumMap<>(Wrapper.class);
return caches;
}
/// Converting references to values.
- static int unboxInteger(Object x) {
- if (x == null) return 0; // never NPE
- return ((Integer) x).intValue();
+ // There are several levels of this unboxing conversions:
+ // no conversions: exactly Integer.valueOf, etc.
+ // implicit conversions sanctioned by JLS 5.1.2, etc.
+ // explicit conversions as allowed by explicitCastArguments
+
+ static int unboxInteger(Object x, boolean cast) {
+ if (x instanceof Integer)
+ return ((Integer) x).intValue();
+ return primitiveConversion(Wrapper.INT, x, cast).intValue();
}
- static byte unboxByte(Object x) {
- if (x == null) return 0; // never NPE
- return ((Byte) x).byteValue();
+ static byte unboxByte(Object x, boolean cast) {
+ if (x instanceof Byte)
+ return ((Byte) x).byteValue();
+ return primitiveConversion(Wrapper.BYTE, x, cast).byteValue();
}
- static short unboxShort(Object x) {
- if (x == null) return 0; // never NPE
- return ((Short) x).shortValue();
+ static short unboxShort(Object x, boolean cast) {
+ if (x instanceof Short)
+ return ((Short) x).shortValue();
+ return primitiveConversion(Wrapper.SHORT, x, cast).shortValue();
}
- static boolean unboxBoolean(Object x) {
- if (x == null) return false; // never NPE
- return ((Boolean) x).booleanValue();
+ static boolean unboxBoolean(Object x, boolean cast) {
+ if (x instanceof Boolean)
+ return ((Boolean) x).booleanValue();
+ return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0;
}
- static char unboxCharacter(Object x) {
- if (x == null) return 0; // never NPE
- return ((Character) x).charValue();
+ static char unboxCharacter(Object x, boolean cast) {
+ if (x instanceof Character)
+ return ((Character) x).charValue();
+ return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue();
}
- static long unboxLong(Object x) {
- if (x == null) return 0; // never NPE
- return ((Long) x).longValue();
+ static long unboxLong(Object x, boolean cast) {
+ if (x instanceof Long)
+ return ((Long) x).longValue();
+ return primitiveConversion(Wrapper.LONG, x, cast).longValue();
}
- static float unboxFloat(Object x) {
- if (x == null) return 0; // never NPE
- return ((Float) x).floatValue();
+ static float unboxFloat(Object x, boolean cast) {
+ if (x instanceof Float)
+ return ((Float) x).floatValue();
+ return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue();
}
- static double unboxDouble(Object x) {
- if (x == null) return 0; // never NPE
- return ((Double) x).doubleValue();
+ static double unboxDouble(Object x, boolean cast) {
+ if (x instanceof Double)
+ return ((Double) x).doubleValue();
+ return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue();
}
/// Converting references to "raw" values.
/// A raw primitive value is always an int or long.
- static int unboxByteRaw(Object x) {
- return unboxByte(x);
+ static int unboxByteRaw(Object x, boolean cast) {
+ return unboxByte(x, cast);
}
- static int unboxShortRaw(Object x) {
- return unboxShort(x);
+ static int unboxShortRaw(Object x, boolean cast) {
+ return unboxShort(x, cast);
}
- static int unboxBooleanRaw(Object x) {
- return unboxBoolean(x) ? 1 : 0;
+ static int unboxBooleanRaw(Object x, boolean cast) {
+ return unboxBoolean(x, cast) ? 1 : 0;
}
- static int unboxCharacterRaw(Object x) {
- return unboxCharacter(x);
+ static int unboxCharacterRaw(Object x, boolean cast) {
+ return unboxCharacter(x, cast);
}
- static int unboxFloatRaw(Object x) {
- return Float.floatToIntBits(unboxFloat(x));
+ static int unboxFloatRaw(Object x, boolean cast) {
+ return Float.floatToIntBits(unboxFloat(x, cast));
}
- static long unboxDoubleRaw(Object x) {
- return Double.doubleToRawLongBits(unboxDouble(x));
+ static long unboxDoubleRaw(Object x, boolean cast) {
+ return Double.doubleToRawLongBits(unboxDouble(x, cast));
}
private static MethodType unboxType(Wrapper wrap, boolean raw) {
- return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), wrap.wrapperType());
+ return MethodType.methodType(rawWrapper(wrap, raw).primitiveType(), Object.class, boolean.class);
}
private static final EnumMap<Wrapper, MethodHandle>[]
UNBOX_CONVERSIONS = newWrapperCaches(4);
- private static MethodHandle unbox(Wrapper wrap, boolean exact, boolean raw) {
- EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(exact?1:0)+(raw?2:0)];
+ private static MethodHandle unbox(Wrapper wrap, boolean raw, boolean cast) {
+ EnumMap<Wrapper, MethodHandle> cache = UNBOX_CONVERSIONS[(cast?1:0)+(raw?2:0)];
MethodHandle mh = cache.get(wrap);
if (mh != null) {
return mh;
@@ -136,7 +154,7 @@
mh = raw ? ALWAYS_ZERO : IGNORE; break;
case INT: case LONG:
// these guys don't need separate raw channels
- if (raw) mh = unbox(wrap, exact, false);
+ if (raw) mh = unbox(wrap, false, cast);
break;
}
if (mh != null) {
@@ -146,37 +164,62 @@
// look up the method
String name = "unbox" + wrap.simpleName() + (raw ? "Raw" : "");
MethodType type = unboxType(wrap, raw);
- if (!exact) {
- try {
- // actually, type is wrong; the Java method takes Object
- mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type.erase());
- } catch (ReflectiveOperationException ex) {
- mh = null;
- }
- } else {
- mh = unbox(wrap, !exact, raw).asType(type);
+ try {
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+ } catch (ReflectiveOperationException ex) {
+ mh = null;
}
if (mh != null) {
+ mh = MethodHandles.insertArguments(mh, 1, cast);
cache.put(wrap, mh);
return mh;
}
- throw new IllegalArgumentException("cannot find unbox adapter for " + wrap + (raw ? " (raw)" : ""));
+ throw new IllegalArgumentException("cannot find unbox adapter for " + wrap
+ + (cast ? " (cast)" : "") + (raw ? " (raw)" : ""));
+ }
+
+ public static MethodHandle unboxCast(Wrapper type) {
+ return unbox(type, false, true);
}
- public static MethodHandle unbox(Wrapper type, boolean exact) {
- return unbox(type, exact, false);
+ public static MethodHandle unboxRaw(Wrapper type) {
+ return unbox(type, true, false);
+ }
+
+ public static MethodHandle unbox(Class<?> type) {
+ return unbox(Wrapper.forPrimitiveType(type), false, false);
+ }
+
+ public static MethodHandle unboxCast(Class<?> type) {
+ return unbox(Wrapper.forPrimitiveType(type), false, true);
}
- public static MethodHandle unboxRaw(Wrapper type, boolean exact) {
- return unbox(type, exact, true);
+ public static MethodHandle unboxRaw(Class<?> type) {
+ return unbox(Wrapper.forPrimitiveType(type), true, false);
}
- public static MethodHandle unbox(Class<?> type, boolean exact) {
- return unbox(Wrapper.forPrimitiveType(type), exact, false);
- }
-
- public static MethodHandle unboxRaw(Class<?> type, boolean exact) {
- return unbox(Wrapper.forPrimitiveType(type), exact, true);
+ /// Primitive conversions
+ public static Number primitiveConversion(Wrapper wrap, Object x, boolean cast) {
+ // Maybe merge this code with Wrapper.convert/cast.
+ Number res = null;
+ if (x == null) {
+ if (!cast) return null;
+ x = wrap.zero();
+ }
+ if (x instanceof Number) {
+ res = (Number) x;
+ } else if (x instanceof Boolean) {
+ res = ((boolean)x ? 1 : 0);
+ } else if (x instanceof Character) {
+ res = (int)(char)x;
+ } else {
+ // this will fail with the required ClassCastException:
+ res = (Number) x;
+ }
+ if (!cast && !wrap.isConvertibleFrom(Wrapper.forWrapperType(x.getClass())))
+ // this will fail with the required ClassCastException:
+ res = (Number) wrap.wrapperType().cast(x);
+ return res;
}
/// Converting primitives to references
@@ -285,7 +328,7 @@
MethodType type = boxType(wrap, raw);
if (exact) {
try {
- mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
} catch (ReflectiveOperationException ex) {
mh = null;
}
@@ -296,22 +339,31 @@
cache.put(wrap, mh);
return mh;
}
- throw new IllegalArgumentException("cannot find box adapter for " + wrap + (raw ? " (raw)" : ""));
+ throw new IllegalArgumentException("cannot find box adapter for "
+ + wrap + (exact ? " (exact)" : "") + (raw ? " (raw)" : ""));
}
- public static MethodHandle box(Class<?> type, boolean exact) {
+ public static MethodHandle box(Class<?> type) {
+ boolean exact = false;
+ // e.g., boxShort(short)Short if exact,
+ // e.g., boxShort(short)Object if !exact
return box(Wrapper.forPrimitiveType(type), exact, false);
}
- public static MethodHandle boxRaw(Class<?> type, boolean exact) {
+ public static MethodHandle boxRaw(Class<?> type) {
+ boolean exact = false;
+ // e.g., boxShortRaw(int)Short if exact
+ // e.g., boxShortRaw(int)Object if !exact
return box(Wrapper.forPrimitiveType(type), exact, true);
}
- public static MethodHandle box(Wrapper type, boolean exact) {
+ public static MethodHandle box(Wrapper type) {
+ boolean exact = false;
return box(type, exact, false);
}
- public static MethodHandle boxRaw(Wrapper type, boolean exact) {
+ public static MethodHandle boxRaw(Wrapper type) {
+ boolean exact = false;
return box(type, exact, true);
}
@@ -319,16 +371,16 @@
static int unboxRawInteger(Object x) {
if (x instanceof Integer)
- return unboxInteger(x);
+ return (int) x;
else
- return (int) unboxLong(x);
+ return (int) unboxLong(x, false);
}
static Integer reboxRawInteger(Object x) {
if (x instanceof Integer)
return (Integer) x;
else
- return (int) unboxLong(x);
+ return (int) unboxLong(x, false);
}
static Byte reboxRawByte(Object x) {
@@ -362,7 +414,7 @@
static Double reboxRawDouble(Object x) {
if (x instanceof Double) return (Double) x;
- return boxDoubleRaw(unboxLong(x));
+ return boxDoubleRaw(unboxLong(x, true));
}
private static MethodType reboxType(Wrapper wrap) {
@@ -371,7 +423,7 @@
}
private static final EnumMap<Wrapper, MethodHandle>[]
- REBOX_CONVERSIONS = newWrapperCaches(2);
+ REBOX_CONVERSIONS = newWrapperCaches(1);
/**
* Because we normalize primitive types to reduce the number of signatures,
@@ -380,10 +432,10 @@
* When the erased primitive value is then boxed into an Integer or Long,
* the final boxed primitive is sometimes required. This transformation
* is called a "rebox". It takes an Integer or Long and produces some
- * other boxed value.
+ * other boxed value, typed (inexactly) as an Object
*/
- public static MethodHandle rebox(Wrapper wrap, boolean exact) {
- EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[exact?1:0];
+ public static MethodHandle rebox(Wrapper wrap) {
+ EnumMap<Wrapper, MethodHandle> cache = REBOX_CONVERSIONS[0];
MethodHandle mh = cache.get(wrap);
if (mh != null) {
return mh;
@@ -402,14 +454,11 @@
// look up the method
String name = "reboxRaw" + wrap.simpleName();
MethodType type = reboxType(wrap);
- if (exact) {
- try {
- mh = IMPL_LOOKUP.findStatic(ValueConversions.class, name, type);
- } catch (ReflectiveOperationException ex) {
- mh = null;
- }
- } else {
- mh = rebox(wrap, !exact).asType(IDENTITY.type());
+ try {
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+ mh = mh.asType(IDENTITY.type());
+ } catch (ReflectiveOperationException ex) {
+ mh = null;
}
if (mh != null) {
cache.put(wrap, mh);
@@ -418,8 +467,8 @@
throw new IllegalArgumentException("cannot find rebox adapter for " + wrap);
}
- public static MethodHandle rebox(Class<?> type, boolean exact) {
- return rebox(Wrapper.forPrimitiveType(type), exact);
+ public static MethodHandle rebox(Class<?> type) {
+ return rebox(Wrapper.forPrimitiveType(type));
}
/// Width-changing conversions between int and long.
@@ -486,9 +535,10 @@
case VOID:
mh = EMPTY;
break;
+ case OBJECT:
case INT: case LONG: case FLOAT: case DOUBLE:
try {
- mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "zero"+wrap.simpleName(), type);
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "zero"+wrap.simpleName(), type);
} catch (ReflectiveOperationException ex) {
mh = null;
}
@@ -592,7 +642,7 @@
return t.cast(x);
}
- private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY;
+ private static final MethodHandle IDENTITY, IDENTITY_I, IDENTITY_J, CAST_REFERENCE, ALWAYS_NULL, ALWAYS_ZERO, ZERO_OBJECT, IGNORE, EMPTY, NEW_ARRAY;
static {
try {
MethodType idType = MethodType.genericMethodType(1);
@@ -600,40 +650,56 @@
MethodType alwaysZeroType = idType.changeReturnType(int.class);
MethodType ignoreType = idType.changeReturnType(void.class);
MethodType zeroObjectType = MethodType.genericMethodType(0);
- IDENTITY = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", idType);
- IDENTITY_I = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(int.class, int.class));
- IDENTITY_J = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", MethodType.methodType(long.class, long.class));
+ IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType);
+ IDENTITY_I = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(int.class, int.class));
+ IDENTITY_J = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(long.class, long.class));
//CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType);
- CAST_REFERENCE = IMPL_LOOKUP.findStatic(ValueConversions.class, "castReference", castType);
- ALWAYS_NULL = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysNull", idType);
- ALWAYS_ZERO = IMPL_LOOKUP.findStatic(ValueConversions.class, "alwaysZero", alwaysZeroType);
- ZERO_OBJECT = IMPL_LOOKUP.findStatic(ValueConversions.class, "zeroObject", zeroObjectType);
- IGNORE = IMPL_LOOKUP.findStatic(ValueConversions.class, "ignore", ignoreType);
- EMPTY = IMPL_LOOKUP.findStatic(ValueConversions.class, "empty", ignoreType.dropParameterTypes(0, 1));
- } catch (Exception ex) {
+ CAST_REFERENCE = IMPL_LOOKUP.findStatic(THIS_CLASS, "castReference", castType);
+ ALWAYS_NULL = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysNull", idType);
+ ALWAYS_ZERO = IMPL_LOOKUP.findStatic(THIS_CLASS, "alwaysZero", alwaysZeroType);
+ ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType);
+ IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType);
+ EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1));
+ NEW_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "newArray", MethodType.methodType(Object[].class, int.class));
+ } catch (NoSuchMethodException | IllegalAccessException ex) {
Error err = new InternalError("uncaught exception");
err.initCause(ex);
throw err;
}
}
- private static final EnumMap<Wrapper, MethodHandle> WRAPPER_CASTS
- = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
+ // Varargs methods need to be in a separately initialized class, to bootstrapping problems.
+ static class LazyStatics {
+ private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST;
+ static {
+ try {
+ //MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class));
+ COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class));
+ COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class));
+ MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class));
+ } catch (ReflectiveOperationException ex) {
+ Error err = new InternalError("uncaught exception");
+ err.initCause(ex);
+ throw err;
+ }
+ }
+ }
- private static final EnumMap<Wrapper, MethodHandle> EXACT_WRAPPER_CASTS
- = new EnumMap<Wrapper, MethodHandle>(Wrapper.class);
+ private static final EnumMap<Wrapper, MethodHandle>[] WRAPPER_CASTS
+ = newWrapperCaches(2);
/** Return a method that casts its sole argument (an Object) to the given type
* and returns it as the given type (if exact is true), or as plain Object (if erase is true).
*/
- public static MethodHandle cast(Class<?> type, boolean exact) {
+ public static MethodHandle cast(Class<?> type) {
+ boolean exact = false;
if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type);
MethodHandle mh = null;
Wrapper wrap = null;
EnumMap<Wrapper, MethodHandle> cache = null;
if (Wrapper.isWrapperType(type)) {
wrap = Wrapper.forWrapperType(type);
- cache = (exact ? EXACT_WRAPPER_CASTS : WRAPPER_CASTS);
+ cache = WRAPPER_CASTS[exact?1:0];
mh = cache.get(wrap);
if (mh != null) return mh;
}
@@ -673,7 +739,7 @@
if (wrap != Wrapper.VOID)
type = type.appendParameterTypes(wrap.primitiveType());
try {
- mh = IMPL_LOOKUP.findStatic(ValueConversions.class, "identity", type);
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type);
} catch (ReflectiveOperationException ex) {
mh = null;
}
@@ -692,6 +758,210 @@
throw new IllegalArgumentException("cannot find identity for " + wrap);
}
+ /// Float/non-float conversions.
+
+ static float doubleToFloat(double x) {
+ return (float) x;
+ }
+ static double floatToDouble(float x) {
+ return x;
+ }
+
+ // narrow double to integral type
+ static long doubleToLong(double x) {
+ return (long) x;
+ }
+ static int doubleToInt(double x) {
+ return (int) x;
+ }
+ static short doubleToShort(double x) {
+ return (short) x;
+ }
+ static char doubleToChar(double x) {
+ return (char) x;
+ }
+ static byte doubleToByte(double x) {
+ return (byte) x;
+ }
+ static boolean doubleToBoolean(double x) {
+ return toBoolean((byte) x);
+ }
+
+ // narrow float to integral type
+ static long floatToLong(float x) {
+ return (long) x;
+ }
+ static int floatToInt(float x) {
+ return (int) x;
+ }
+ static short floatToShort(float x) {
+ return (short) x;
+ }
+ static char floatToChar(float x) {
+ return (char) x;
+ }
+ static byte floatToByte(float x) {
+ return (byte) x;
+ }
+ static boolean floatToBoolean(float x) {
+ return toBoolean((byte) x);
+ }
+
+ // widen integral type to double
+ static double longToDouble(long x) {
+ return x;
+ }
+ static double intToDouble(int x) {
+ return x;
+ }
+ static double shortToDouble(short x) {
+ return x;
+ }
+ static double charToDouble(char x) {
+ return x;
+ }
+ static double byteToDouble(byte x) {
+ return x;
+ }
+ static double booleanToDouble(boolean x) {
+ return fromBoolean(x);
+ }
+
+ // widen integral type to float
+ static float longToFloat(long x) {
+ return x;
+ }
+ static float intToFloat(int x) {
+ return x;
+ }
+ static float shortToFloat(short x) {
+ return x;
+ }
+ static float charToFloat(char x) {
+ return x;
+ }
+ static float byteToFloat(byte x) {
+ return x;
+ }
+ static float booleanToFloat(boolean x) {
+ return fromBoolean(x);
+ }
+
+ static boolean toBoolean(byte x) {
+ // see javadoc for MethodHandles.explicitCastArguments
+ return ((x & 1) != 0);
+ }
+ static byte fromBoolean(boolean x) {
+ // see javadoc for MethodHandles.explicitCastArguments
+ return (x ? (byte)1 : (byte)0);
+ }
+
+ private static final EnumMap<Wrapper, MethodHandle>[]
+ CONVERT_FLOAT_FUNCTIONS = newWrapperCaches(4);
+
+ static MethodHandle convertFloatFunction(Wrapper wrap, boolean toFloat, boolean doubleSize) {
+ EnumMap<Wrapper, MethodHandle> cache = CONVERT_FLOAT_FUNCTIONS[(toFloat?1:0)+(doubleSize?2:0)];
+ MethodHandle mh = cache.get(wrap);
+ if (mh != null) {
+ return mh;
+ }
+ // slow path
+ Wrapper fwrap = (doubleSize ? Wrapper.DOUBLE : Wrapper.FLOAT);
+ Class<?> fix = wrap.primitiveType();
+ Class<?> flt = (doubleSize ? double.class : float.class);
+ Class<?> src = toFloat ? fix : flt;
+ Class<?> dst = toFloat ? flt : fix;
+ if (src == dst) return identity(wrap);
+ MethodType type = MethodType.methodType(dst, src);
+ switch (wrap) {
+ case VOID:
+ mh = toFloat ? zeroConstantFunction(fwrap) : MethodHandles.dropArguments(EMPTY, 0, flt);
+ break;
+ case OBJECT:
+ mh = toFloat ? unbox(flt) : box(flt);
+ break;
+ default:
+ try {
+ mh = IMPL_LOOKUP.findStatic(THIS_CLASS, src.getSimpleName()+"To"+capitalize(dst.getSimpleName()), type);
+ } catch (ReflectiveOperationException ex) {
+ mh = null;
+ }
+ break;
+ }
+ if (mh != null) {
+ assert(mh.type() == type) : mh;
+ cache.put(wrap, mh);
+ return mh;
+ }
+
+ throw new IllegalArgumentException("cannot find float conversion constant for " +
+ src.getSimpleName()+" -> "+dst.getSimpleName());
+ }
+
+ public static MethodHandle convertFromFloat(Class<?> fixType) {
+ Wrapper wrap = Wrapper.forPrimitiveType(fixType);
+ return convertFloatFunction(wrap, false, false);
+ }
+ public static MethodHandle convertFromDouble(Class<?> fixType) {
+ Wrapper wrap = Wrapper.forPrimitiveType(fixType);
+ return convertFloatFunction(wrap, false, true);
+ }
+ public static MethodHandle convertToFloat(Class<?> fixType) {
+ Wrapper wrap = Wrapper.forPrimitiveType(fixType);
+ return convertFloatFunction(wrap, true, false);
+ }
+ public static MethodHandle convertToDouble(Class<?> fixType) {
+ Wrapper wrap = Wrapper.forPrimitiveType(fixType);
+ return convertFloatFunction(wrap, true, true);
+ }
+
+ private static String capitalize(String x) {
+ return Character.toUpperCase(x.charAt(0))+x.substring(1);
+ }
+
+ /// Collection of multiple arguments.
+
+ public static Object convertArrayElements(Class<?> arrayType, Object array) {
+ Class<?> src = array.getClass().getComponentType();
+ Class<?> dst = arrayType.getComponentType();
+ if (src == null || dst == null) throw new IllegalArgumentException("not array type");
+ Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null);
+ Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null);
+ int length;
+ if (sw == null) {
+ Object[] a = (Object[]) array;
+ length = a.length;
+ if (dw == null)
+ return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class));
+ Object res = dw.makeArray(length);
+ dw.copyArrayUnboxing(a, 0, res, 0, length);
+ return res;
+ }
+ length = java.lang.reflect.Array.getLength(array);
+ Object[] res;
+ if (dw == null) {
+ res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class));
+ } else {
+ res = new Object[length];
+ }
+ sw.copyArrayBoxing(array, 0, res, 0, length);
+ if (dw == null) return res;
+ Object a = dw.makeArray(length);
+ dw.copyArrayUnboxing(res, 0, a, 0, length);
+ return a;
+ }
+
+ private static MethodHandle findCollector(String name, int nargs, Class<?> rtype, Class<?>... ptypes) {
+ MethodType type = MethodType.genericMethodType(nargs)
+ .changeReturnType(rtype)
+ .insertParameterTypes(0, ptypes);
+ try {
+ return IMPL_LOOKUP.findStatic(THIS_CLASS, name, type);
+ } catch (ReflectiveOperationException ex) {
+ return null;
+ }
+ }
+
private static final Object[] NO_ARGS_ARRAY = {};
private static Object[] makeArray(Object... args) { return args; }
private static Object[] array() { return NO_ARGS_ARRAY; }
@@ -723,35 +993,175 @@
Object a4, Object a5, Object a6, Object a7,
Object a8, Object a9)
{ return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
- static MethodHandle[] makeArrays() {
- ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>();
- MethodHandles.Lookup lookup = IMPL_LOOKUP;
+ private static MethodHandle[] makeArrays() {
+ ArrayList<MethodHandle> mhs = new ArrayList<>();
for (;;) {
- int nargs = arrays.size();
- MethodType type = MethodType.genericMethodType(nargs).changeReturnType(Object[].class);
- String name = "array";
- MethodHandle array = null;
- try {
- array = lookup.findStatic(ValueConversions.class, name, type);
- } catch (ReflectiveOperationException ex) {
- }
- if (array == null) break;
- arrays.add(array);
+ MethodHandle mh = findCollector("array", mhs.size(), Object[].class);
+ if (mh == null) break;
+ mhs.add(mh);
}
- assert(arrays.size() == 11); // current number of methods
- return arrays.toArray(new MethodHandle[0]);
+ assert(mhs.size() == 11); // current number of methods
+ return mhs.toArray(new MethodHandle[MAX_ARITY+1]);
+ }
+ private static final MethodHandle[] ARRAYS = makeArrays();
+
+ // mh-fill versions of the above:
+ private static Object[] newArray(int len) { return new Object[len]; }
+ private static void fillWithArguments(Object[] a, int pos, Object... args) {
+ System.arraycopy(args, 0, a, pos, args.length);
}
- static final MethodHandle[] ARRAYS = makeArrays();
+ // using Integer pos instead of int pos to avoid bootstrapping problems
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0)
+ { fillWithArguments(a, pos, a0); return a; }
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1)
+ { fillWithArguments(a, pos, a0, a1); return a; }
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2)
+ { fillWithArguments(a, pos, a0, a1, a2); return a; }
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3)
+ { fillWithArguments(a, pos, a0, a1, a2, a3); return a; }
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ Object a4)
+ { fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; }
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5)
+ { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; }
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6)
+ { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; }
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6, Object a7)
+ { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; }
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6, Object a7,
+ Object a8)
+ { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; }
+ private static Object[] fillArray(Object[] a, Integer pos, Object a0, Object a1, Object a2, Object a3,
+ Object a4, Object a5, Object a6, Object a7,
+ Object a8, Object a9)
+ { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
+ private static MethodHandle[] makeFillArrays() {
+ ArrayList<MethodHandle> mhs = new ArrayList<>();
+ mhs.add(null); // there is no empty fill; at least a0 is required
+ for (;;) {
+ MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Object[].class, Integer.class);
+ if (mh == null) break;
+ mhs.add(mh);
+ }
+ assert(mhs.size() == 11); // current number of methods
+ return mhs.toArray(new MethodHandle[0]);
+ }
+ private static final MethodHandle[] FILL_ARRAYS = makeFillArrays();
+
+ private static Object[] copyAsReferenceArray(Class<? extends Object[]> arrayType, Object... a) {
+ return Arrays.copyOf(a, a.length, arrayType);
+ }
+ private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
+ Object a = w.makeArray(boxes.length);
+ w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length);
+ return a;
+ }
/** Return a method handle that takes the indicated number of Object
* arguments and returns an Object array of them, as if for varargs.
*/
public static MethodHandle varargsArray(int nargs) {
- if (nargs < ARRAYS.length)
- return ARRAYS[nargs];
- // else need to spin bytecode or do something else fancy
- throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
+ MethodHandle mh = ARRAYS[nargs];
+ if (mh != null) return mh;
+ mh = findCollector("array", nargs, Object[].class);
+ if (mh != null) return ARRAYS[nargs] = mh;
+ MethodHandle producer = filler(0); // identity function produces result
+ return ARRAYS[nargs] = buildVarargsArray(producer, nargs);
+ }
+
+ private static MethodHandle buildVarargsArray(MethodHandle producer, int nargs) {
+ // Build up the result mh as a sequence of fills like this:
+ // producer(fill(fill(fill(newArray(23),0,x1..x10),10,x11..x20),20,x21..x23))
+ // The various fill(_,10*I,___*[J]) are reusable.
+ MethodHandle filler = filler(nargs);
+ MethodHandle mh = producer;
+ mh = MethodHandles.dropArguments(mh, 1, filler.type().parameterList());
+ mh = MethodHandles.foldArguments(mh, filler);
+ mh = MethodHandles.foldArguments(mh, buildNewArray(nargs));
+ return mh;
+ }
+
+ private static MethodHandle buildNewArray(int nargs) {
+ return MethodHandles.insertArguments(NEW_ARRAY, 0, (int) nargs);
+ }
+
+ private static final MethodHandle[] FILLERS = new MethodHandle[MAX_ARITY+1];
+ // filler(N).invoke(a, arg0..arg[N-1]) fills a[0]..a[N-1]
+ private static MethodHandle filler(int nargs) {
+ MethodHandle filler = FILLERS[nargs];
+ if (filler != null) return filler;
+ return FILLERS[nargs] = buildFiller(nargs);
}
+ private static MethodHandle buildFiller(int nargs) {
+ if (nargs == 0)
+ return MethodHandles.identity(Object[].class);
+ final int CHUNK = (FILL_ARRAYS.length - 1);
+ int rightLen = nargs % CHUNK;
+ int leftLen = nargs - rightLen;
+ if (rightLen == 0) {
+ leftLen = nargs - (rightLen = CHUNK);
+ if (FILLERS[leftLen] == null) {
+ // build some precursors from left to right
+ for (int j = 0; j < leftLen; j += CHUNK) filler(j);
+ }
+ }
+ MethodHandle leftFill = filler(leftLen); // recursive fill
+ MethodHandle rightFill = FILL_ARRAYS[rightLen];
+ rightFill = MethodHandles.insertArguments(rightFill, 1, (int) leftLen); // [leftLen..nargs-1]
+
+ // Combine the two fills: right(left(newArray(nargs), x1..x20), x21..x23)
+ MethodHandle mh = filler(0); // identity function produces result
+ mh = MethodHandles.dropArguments(mh, 1, rightFill.type().parameterList());
+ mh = MethodHandles.foldArguments(mh, rightFill);
+ if (leftLen > 0) {
+ mh = MethodHandles.dropArguments(mh, 1, leftFill.type().parameterList());
+ mh = MethodHandles.foldArguments(mh, leftFill);
+ }
+ return mh;
+ }
+
+ // Type-polymorphic version of varargs maker.
+ private static final ClassValue<MethodHandle[]> TYPED_COLLECTORS
+ = new ClassValue<MethodHandle[]>() {
+ protected MethodHandle[] computeValue(Class<?> type) {
+ return new MethodHandle[256];
+ }
+ };
+
+ /** Return a method handle that takes the indicated number of
+ * typed arguments and returns an array of them.
+ * The type argument is the array type.
+ */
+ public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
+ Class<?> elemType = arrayType.getComponentType();
+ if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType);
+ // FIXME: Need more special casing and caching here.
+ if (elemType == Object.class)
+ return varargsArray(nargs);
+ // other cases: primitive arrays, subtypes of Object[]
+ MethodHandle cache[] = TYPED_COLLECTORS.get(elemType);
+ MethodHandle mh = nargs < cache.length ? cache[nargs] : null;
+ if (mh != null) return mh;
+ MethodHandle producer = buildArrayProducer(arrayType);
+ mh = buildVarargsArray(producer, nargs);
+ mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
+ cache[nargs] = mh;
+ return mh;
+ }
+
+ private static MethodHandle buildArrayProducer(Class<?> arrayType) {
+ Class<?> elemType = arrayType.getComponentType();
+ if (elemType.isPrimitive())
+ return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType));
+ else
+ return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType);
+ }
+
+ // List version of varargs maker.
private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
@@ -784,34 +1194,29 @@
Object a4, Object a5, Object a6, Object a7,
Object a8, Object a9)
{ return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
- static MethodHandle[] makeLists() {
- ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>();
- MethodHandles.Lookup lookup = IMPL_LOOKUP;
+ private static MethodHandle[] makeLists() {
+ ArrayList<MethodHandle> mhs = new ArrayList<>();
for (;;) {
- int nargs = arrays.size();
- MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class);
- String name = "list";
- MethodHandle array = null;
- try {
- array = lookup.findStatic(ValueConversions.class, name, type);
- } catch (ReflectiveOperationException ex) {
- }
- if (array == null) break;
- arrays.add(array);
+ MethodHandle mh = findCollector("list", mhs.size(), List.class);
+ if (mh == null) break;
+ mhs.add(mh);
}
- assert(arrays.size() == 11); // current number of methods
- return arrays.toArray(new MethodHandle[0]);
+ assert(mhs.size() == 11); // current number of methods
+ return mhs.toArray(new MethodHandle[MAX_ARITY+1]);
}
- static final MethodHandle[] LISTS = makeLists();
+ private static final MethodHandle[] LISTS = makeLists();
/** Return a method handle that takes the indicated number of Object
- * arguments and returns List.
+ * arguments and returns a List.
*/
public static MethodHandle varargsList(int nargs) {
- if (nargs < LISTS.length)
- return LISTS[nargs];
- // else need to spin bytecode or do something else fancy
- throw new UnsupportedOperationException("NYI");
+ MethodHandle mh = LISTS[nargs];
+ if (mh != null) return mh;
+ mh = findCollector("list", nargs, List.class);
+ if (mh != null) return LISTS[nargs] = mh;
+ return LISTS[nargs] = buildVarargsList(nargs);
+ }
+ private static MethodHandle buildVarargsList(int nargs) {
+ return MethodHandles.filterReturnValue(varargsArray(nargs), LazyStatics.MAKE_LIST);
}
}
-
--- a/jdk/src/share/classes/sun/invoke/util/VerifyType.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/VerifyType.java Tue May 24 14:32:10 2011 -0700
@@ -54,9 +54,15 @@
if (dst == void.class) return true; // drop any return value
if (isNullType(src)) return !dst.isPrimitive();
if (!src.isPrimitive()) return dst.isAssignableFrom(src);
+ if (!dst.isPrimitive()) return false;
// Verifier allows an int to carry byte, short, char, or even boolean:
- if (dst == int.class) return Wrapper.forPrimitiveType(src).isSubwordOrInt();
- return false;
+ Wrapper sw = Wrapper.forPrimitiveType(src);
+ if (dst == int.class) return sw.isSubwordOrInt();
+ Wrapper dw = Wrapper.forPrimitiveType(dst);
+ if (!sw.isSubwordOrInt()) return false;
+ if (!dw.isSubwordOrInt()) return false;
+ if (!dw.isSigned() && sw.isSigned()) return false;
+ return dw.bitWidth() > sw.bitWidth();
}
/**
@@ -155,6 +161,7 @@
return -1; // truncation may be required
if (!dw.isSigned() && sw.isSigned())
return -1; // sign elimination may be required
+ return 1;
}
if (src == float.class || dst == float.class) {
if (src == double.class || dst == double.class)
--- a/jdk/src/share/classes/sun/invoke/util/Wrapper.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/invoke/util/Wrapper.java Tue May 24 14:32:10 2011 -0700
@@ -26,37 +26,47 @@
package sun.invoke.util;
public enum Wrapper {
- BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, Format.unsigned(1)),
+ BOOLEAN(Boolean.class, boolean.class, 'Z', (Boolean)false, new boolean[0], Format.unsigned(1)),
// These must be in the order defined for widening primitive conversions in JLS 5.1.2
- BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, Format.signed(8)),
- SHORT(Short.class, short.class, 'S', (Short)(short)0, Format.signed(16)),
- CHAR(Character.class, char.class, 'C', (Character)(char)0, Format.unsigned(16)),
- INT(Integer.class, int.class, 'I', (Integer)(int)0, Format.signed(32)),
- LONG(Long.class, long.class, 'J', (Long)(long)0, Format.signed(64)),
- FLOAT(Float.class, float.class, 'F', (Float)(float)0, Format.floating(32)),
- DOUBLE(Double.class, double.class, 'D', (Double)(double)0, Format.floating(64)),
- //NULL(Null.class, null.class, 'N', null, Format.other(1)),
- OBJECT(Object.class, Object.class, 'L', null, Format.other(1)),
+ BYTE(Byte.class, byte.class, 'B', (Byte)(byte)0, new byte[0], Format.signed(8)),
+ SHORT(Short.class, short.class, 'S', (Short)(short)0, new short[0], Format.signed(16)),
+ CHAR(Character.class, char.class, 'C', (Character)(char)0, new char[0], Format.unsigned(16)),
+ INT(Integer.class, int.class, 'I', (Integer)(int)0, new int[0], Format.signed(32)),
+ LONG(Long.class, long.class, 'J', (Long)(long)0, new long[0], Format.signed(64)),
+ FLOAT(Float.class, float.class, 'F', (Float)(float)0, new float[0], Format.floating(32)),
+ DOUBLE(Double.class, double.class, 'D', (Double)(double)0, new double[0], Format.floating(64)),
+ //NULL(Null.class, null.class, 'N', null, null, Format.other(1)),
+ OBJECT(Object.class, Object.class, 'L', null, new Object[0], Format.other(1)),
// VOID must be the last type, since it is "assignable" from any other type:
- VOID(Void.class, void.class, 'V', null, Format.other(0)),
+ VOID(Void.class, void.class, 'V', null, null, Format.other(0)),
;
private final Class<?> wrapperType;
private final Class<?> primitiveType;
private final char basicTypeChar;
private final Object zero;
+ private final Object emptyArray;
private final int format;
private final String simpleName;
- private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, int format) {
+ private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, Object emptyArray, int format) {
this.wrapperType = wtype;
this.primitiveType = ptype;
this.basicTypeChar = tchar;
this.zero = zero;
+ this.emptyArray = emptyArray;
this.format = format;
this.simpleName = wtype.getSimpleName();
}
+ /** For debugging, give the details of this wrapper. */
+ public String detailString() {
+ return simpleName+
+ java.util.Arrays.asList(wrapperType, primitiveType,
+ basicTypeChar, zero,
+ "0x"+Integer.toHexString(format));
+ }
+
private static abstract class Format {
static final int SLOT_SHIFT = 0, SIZE_SHIFT = 2, KIND_SHIFT = 12;
static final int
@@ -114,16 +124,18 @@
public boolean isUnsigned() { return format >= Format.BOOLEAN && format < Format.FLOAT; }
/** Is the wrapped type either float or double? */
public boolean isFloating() { return format >= Format.FLOAT; }
+ /** Is the wrapped type either void or a reference? */
+ public boolean isOther() { return (format & ~Format.SLOT_MASK) == 0; }
- /** Does the JVM verifier allow a variable of this wrapper's
+ /** Does the JLS 5.1.2 allow a variable of this wrapper's
* primitive type to be assigned from a value of the given wrapper's primitive type?
* Cases:
* <ul>
* <li>unboxing followed by widening primitive conversion
- * <li>any type converted to {@code void}
+ * <li>any type converted to {@code void} (i.e., dropping a method call's value)
* <li>boxing conversion followed by widening reference conversion to {@code Object}
- * <li>conversion of {@code boolean} to any type
* </ul>
+ * These are the cases allowed by MethodHandle.asType and convertArguments.
*/
public boolean isConvertibleFrom(Wrapper source) {
if (this == source) return true;
@@ -131,13 +143,75 @@
// At best, this is a narrowing conversion.
return false;
}
- if ((this.format ^ source.format) == (Format.SHORT ^ Format.CHAR)) {
- assert (this == SHORT && source == CHAR) || (this == CHAR && source == SHORT);
+ // All conversions are allowed in the enum order between floats and signed ints.
+ // First detect non-signed non-float types (boolean, char, Object, void).
+ boolean floatOrSigned = (((this.format & source.format) & Format.SIGNED) != 0);
+ if (!floatOrSigned) {
+ if (this.isOther()) return true;
+ // can convert char to int or wider, but nothing else
+ if (source.format == Format.CHAR) return true;
+ // no other conversions are classified as widening
return false;
}
+ // All signed and float conversions in the enum order are widening.
+ assert(this.isFloating() || this.isSigned());
+ assert(source.isFloating() || source.isSigned());
return true;
}
+ static { assert(checkConvertibleFrom()); }
+ private static boolean checkConvertibleFrom() {
+ // Check the matrix for correct classification of widening conversions.
+ for (Wrapper w : values()) {
+ assert(w.isConvertibleFrom(w));
+ assert(VOID.isConvertibleFrom(w));
+ if (w != VOID) {
+ assert(OBJECT.isConvertibleFrom(w));
+ assert(!w.isConvertibleFrom(VOID));
+ }
+ // check relations with unsigned integral types:
+ if (w != CHAR) {
+ assert(!CHAR.isConvertibleFrom(w));
+ if (!w.isConvertibleFrom(INT))
+ assert(!w.isConvertibleFrom(CHAR));
+ }
+ if (w != BOOLEAN) {
+ assert(!BOOLEAN.isConvertibleFrom(w));
+ if (w != VOID && w != OBJECT)
+ assert(!w.isConvertibleFrom(BOOLEAN));
+ }
+ // check relations with signed integral types:
+ if (w.isSigned()) {
+ for (Wrapper x : values()) {
+ if (w == x) continue;
+ if (x.isFloating())
+ assert(!w.isConvertibleFrom(x));
+ else if (x.isSigned()) {
+ if (w.compareTo(x) < 0)
+ assert(!w.isConvertibleFrom(x));
+ else
+ assert(w.isConvertibleFrom(x));
+ }
+ }
+ }
+ // check relations with floating types:
+ if (w.isFloating()) {
+ for (Wrapper x : values()) {
+ if (w == x) continue;
+ if (x.isSigned())
+ assert(w.isConvertibleFrom(x));
+ else if (x.isFloating()) {
+ if (w.compareTo(x) < 0)
+ assert(!w.isConvertibleFrom(x));
+ else
+ assert(w.isConvertibleFrom(x));
+ }
+ }
+ }
+ }
+ return true; // i.e., assert(true)
+ }
+
/** Produce a zero value for the given wrapper type.
* This will be a numeric zero for a number or character,
* false for a boolean, and null for a reference or void.
@@ -549,7 +623,7 @@
}
private static boolean boolValue(long bits) {
- //bits &= 1; // simple 31-bit zero extension
+ bits &= 1; // simple 31-bit zero extension
return (bits != 0);
}
@@ -559,4 +633,31 @@
private static RuntimeException newIllegalArgumentException(String message) {
return new IllegalArgumentException(message);
}
+
+ // primitive array support
+ public Object makeArray(int len) {
+ return java.lang.reflect.Array.newInstance(primitiveType, len);
+ }
+ public Class<?> arrayType() {
+ return emptyArray.getClass();
+ }
+ public void copyArrayUnboxing(Object[] values, int vpos, Object a, int apos, int length) {
+ if (a.getClass() != arrayType())
+ arrayType().cast(a); // throw NPE or CCE if bad type
+ for (int i = 0; i < length; i++) {
+ Object value = values[i+vpos];
+ value = convert(value, primitiveType);
+ java.lang.reflect.Array.set(a, i+apos, value);
+ }
+ }
+ public void copyArrayBoxing(Object a, int apos, Object[] values, int vpos, int length) {
+ if (a.getClass() != arrayType())
+ arrayType().cast(a); // throw NPE or CCE if bad type
+ for (int i = 0; i < length; i++) {
+ Object value = java.lang.reflect.Array.get(a, i+apos);
+ //Already done: value = convert(value, primitiveType);
+ assert(value.getClass() == wrapperType);
+ values[i+vpos] = value;
+ }
+ }
}
--- a/jdk/src/share/classes/sun/java2d/opengl/OGLRenderer.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/java2d/opengl/OGLRenderer.java Tue May 24 14:32:10 2011 -0700
@@ -102,15 +102,20 @@
final ParallelogramPipe realpipe = oglr.getAAParallelogramPipe();
return new ParallelogramPipe() {
public void fillParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
{
GraphicsPrimitive.tracePrimitive("OGLFillAAParallelogram");
realpipe.fillParallelogram(sg2d,
+ ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2);
}
public void drawParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
@@ -118,6 +123,7 @@
{
GraphicsPrimitive.tracePrimitive("OGLDrawAAParallelogram");
realpipe.drawParallelogram(sg2d,
+ ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2,
lw1, lw2);
}
@@ -166,21 +172,29 @@
oglr.fillSpans(sg2d, si, transx, transy);
}
public void fillParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
{
GraphicsPrimitive.tracePrimitive("OGLFillParallelogram");
- oglr.fillParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2);
+ oglr.fillParallelogram(sg2d,
+ ux1, uy1, ux2, uy2,
+ x, y, dx1, dy1, dx2, dy2);
}
public void drawParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
double lw1, double lw2)
{
GraphicsPrimitive.tracePrimitive("OGLDrawParallelogram");
- oglr.drawParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2, lw1, lw2);
+ oglr.drawParallelogram(sg2d,
+ ux1, uy1, ux2, uy2,
+ x, y, dx1, dy1, dx2, dy2, lw1, lw2);
}
public void copyArea(SunGraphics2D sg2d,
int x, int y, int w, int h, int dx, int dy)
--- a/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/java2d/pipe/AAShapePipe.java Tue May 24 14:32:10 2011 -0700
@@ -68,21 +68,23 @@
renderPath(sg, s, null);
}
- private static Rectangle2D computeBBox(double x, double y,
- double dx1, double dy1,
- double dx2, double dy2)
+ private static Rectangle2D computeBBox(double ux1, double uy1,
+ double ux2, double uy2)
{
- double lox, loy, hix, hiy;
- lox = hix = x;
- loy = hiy = y;
- if (dx1 < 0) { lox += dx1; } else { hix += dx1; }
- if (dy1 < 0) { loy += dy1; } else { hiy += dy1; }
- if (dx2 < 0) { lox += dx2; } else { hix += dx2; }
- if (dy2 < 0) { loy += dy2; } else { hiy += dy2; }
- return new Rectangle2D.Double(lox, loy, hix-lox, hiy-loy);
+ if ((ux2 -= ux1) < 0) {
+ ux1 += ux2;
+ ux2 = -ux2;
+ }
+ if ((uy2 -= uy1) < 0) {
+ uy1 += uy2;
+ uy2 = -uy2;
+ }
+ return new Rectangle2D.Double(ux1, uy1, ux2, uy2);
}
public void fillParallelogram(SunGraphics2D sg,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
@@ -97,10 +99,12 @@
return;
}
- renderTiles(sg, computeBBox(x, y, dx1, dy1, dx2, dy2), aatg, abox);
+ renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox);
}
public void drawParallelogram(SunGraphics2D sg,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
@@ -118,7 +122,7 @@
// Note that bbox is of the original shape, not the wide path.
// This is appropriate for handing to Paint methods...
- renderTiles(sg, computeBBox(x, y, dx1, dy1, dx2, dy2), aatg, abox);
+ renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox);
}
private static byte[] theTile;
--- a/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/java2d/pipe/AlphaColorPipe.java Tue May 24 14:32:10 2011 -0700
@@ -66,6 +66,8 @@
}
public void fillParallelogram(SunGraphics2D sg,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
@@ -75,6 +77,8 @@
}
public void drawParallelogram(SunGraphics2D sg,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
--- a/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/java2d/pipe/BufferedRenderPipe.java Tue May 24 14:32:10 2011 -0700
@@ -408,6 +408,8 @@
}
public void fillParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
@@ -429,6 +431,8 @@
}
public void drawParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
@@ -454,6 +458,8 @@
private class AAParallelogramPipe implements ParallelogramPipe {
public void fillParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
@@ -475,6 +481,8 @@
}
public void drawParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
--- a/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/java2d/pipe/LoopPipe.java Tue May 24 14:32:10 2011 -0700
@@ -352,6 +352,8 @@
}
public void fillParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
@@ -362,6 +364,8 @@
}
public void drawParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
--- a/jdk/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/java2d/pipe/ParallelogramPipe.java Tue May 24 14:32:10 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011 Oracle and/or its affiliates. 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
@@ -40,9 +40,17 @@
* => (x+dx2, y+dy2)
* => origin
* </pre>
+ * The four u[xy][12] parameters are the unsorted extreme coordinates
+ * of the primitive in user space. They may have been generated by a
+ * line or a rectangle so they could have u[xy]2 < u[xy]1 in some cases.
+ * They should be sorted before calculating the bounds of the original
+ * primitive (such as for calculating the user space bounds for the
+ * Paint.createContext() method).
*/
public interface ParallelogramPipe {
public void fillParallelogram(SunGraphics2D sg,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2);
@@ -59,6 +67,8 @@
* difference between the outer and inner parallelograms.
*/
public void drawParallelogram(SunGraphics2D sg,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
--- a/jdk/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/java2d/pipe/PixelToParallelogramConverter.java Tue May 24 14:32:10 2011 -0700
@@ -175,8 +175,8 @@
}
public boolean drawGeneralLine(SunGraphics2D sg2d,
- double x1, double y1,
- double x2, double y2)
+ double ux1, double uy1,
+ double ux2, double uy2)
{
if (sg2d.strokeState == SunGraphics2D.STROKE_CUSTOM ||
sg2d.strokeState == SunGraphics2D.STROKE_THINDASHED)
@@ -194,13 +194,14 @@
double lw = bs.getLineWidth();
// Save the original dx, dy in case we need it to transform
// the linewidth as a perpendicular vector below
- double dx = x2 - x1;
- double dy = y2 - y1;
+ double dx = ux2 - ux1;
+ double dy = uy2 - uy1;
+ double x1, y1, x2, y2;
switch (sg2d.transformState) {
case SunGraphics2D.TRANSFORM_GENERIC:
case SunGraphics2D.TRANSFORM_TRANSLATESCALE:
{
- double coords[] = {x1, y1, x2, y2};
+ double coords[] = {ux1, uy1, ux2, uy2};
sg2d.transform.transform(coords, 0, coords, 0, 2);
x1 = coords[0];
y1 = coords[1];
@@ -213,13 +214,17 @@
{
double tx = sg2d.transform.getTranslateX();
double ty = sg2d.transform.getTranslateY();
- x1 += tx;
- y1 += ty;
- x2 += tx;
- y2 += ty;
+ x1 = ux1 + tx;
+ y1 = uy1 + ty;
+ x2 = ux2 + tx;
+ y2 = uy2 + ty;
}
break;
case SunGraphics2D.TRANSFORM_ISIDENT:
+ x1 = ux1;
+ y1 = uy1;
+ x2 = ux2;
+ y2 = uy2;
break;
default:
throw new InternalError("unknown TRANSFORM state...");
@@ -279,7 +284,8 @@
dx += udx;
dy += udy;
}
- outrenderer.fillParallelogram(sg2d, px, py, -udy, udx, dx, dy);
+ outrenderer.fillParallelogram(sg2d, ux1, uy1, ux2, uy2,
+ px, py, -udy, udx, dx, dy);
return true;
}
@@ -313,7 +319,8 @@
px = newx;
py = newy;
}
- outrenderer.fillParallelogram(sg2d, px, py, dx1, dy1, dx2, dy2);
+ outrenderer.fillParallelogram(sg2d, rx, ry, rx+rw, ry+rh,
+ px, py, dx1, dy1, dx2, dy2);
}
public void drawRectangle(SunGraphics2D sg2d,
@@ -360,10 +367,12 @@
// entire hole in the middle of the parallelogram
// so we can just fill the outer parallelogram.
fillOuterParallelogram(sg2d,
+ rx, ry, rx+rw, ry+rh,
px, py, dx1, dy1, dx2, dy2,
len1, len2, lw1, lw2);
} else {
outrenderer.drawParallelogram(sg2d,
+ rx, ry, rx+rw, ry+rh,
px, py, dx1, dy1, dx2, dy2,
lw1 / len1, lw2 / len2);
}
@@ -377,6 +386,8 @@
* and issues a single fillParallelogram request to fill it.
*/
public void fillOuterParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double px, double py,
double dx1, double dy1,
double dx2, double dy2,
@@ -412,6 +423,7 @@
dx2 += udx2;
dy2 += udy2;
- outrenderer.fillParallelogram(sg2d, px, py, dx1, dy1, dx2, dy2);
+ outrenderer.fillParallelogram(sg2d, ux1, uy1, ux2, uy2,
+ px, py, dx1, dy1, dx2, dy2);
}
}
--- a/jdk/src/share/classes/sun/text/bidi/BidiBase.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/share/classes/sun/text/bidi/BidiBase.java Tue May 24 14:32:10 2011 -0700
@@ -2889,10 +2889,6 @@
verifyValidPara();
verifyRange(start, 0, limit);
verifyRange(limit, 0, length+1);
- if (getParagraphIndex(start) != getParagraphIndex(limit - 1)) {
- /* the line crosses a paragraph boundary */
- throw new IllegalArgumentException();
- }
return BidiLine.setLine(bidi, this, newBidi, newBidiBase, start, limit);
}
--- a/jdk/src/solaris/classes/sun/awt/X11/XRobotPeer.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/solaris/classes/sun/awt/X11/XRobotPeer.java Tue May 24 14:32:10 2011 -0700
@@ -48,7 +48,7 @@
}
public void dispose() {
- _dispose();
+ // does nothing
}
public void mouseMove(int x, int y) {
@@ -88,7 +88,6 @@
}
private static native synchronized void setup(int numberOfButtons, int[] buttonDownMasks);
- private static native synchronized void _dispose();
private static native synchronized void mouseMoveImpl(X11GraphicsConfig xgc, int x, int y);
private static native synchronized void mousePressImpl(int buttons);
--- a/jdk/src/solaris/native/sun/awt/awt_Robot.c Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/solaris/native/sun/awt/awt_Robot.c Tue May 24 14:32:10 2011 -0700
@@ -48,28 +48,12 @@
#ifdef __linux__
#include <sys/socket.h>
#endif
-#include <dlfcn.h>
extern struct X11GraphicsConfigIDs x11GraphicsConfigIDs;
static jint * masks;
static jint num_buttons;
-static unsigned int s_robotInstanceCounter = 0;
-
-static void* xcompositeLibHandle = NULL;
-static Bool xcompositeExtAvailable = False;
-static Bool xcompositeExtTested = False;
-
-typedef Status (*T_XCompositeQueryVersion)(Display *dpy, int *major_versionp, int *minor_versionp);
-typedef Window (*T_XCompositeGetOverlayWindow)(Display *dpy, Window window);
-typedef void (*T_XCompositeReleaseOverlayWindow)(Display *dpy, Window window);
-
-static T_XCompositeQueryVersion XCompositeQueryVersion = NULL;
-static T_XCompositeGetOverlayWindow XCompositeGetOverlayWindow = NULL;
-static T_XCompositeReleaseOverlayWindow XCompositeReleaseOverlayWindow = NULL;
-
-
static int32_t isXTestAvailable() {
int32_t major_opcode, first_event, first_error;
int32_t event_basep, error_basep, majorp, minorp;
@@ -210,80 +194,8 @@
}
AWT_UNLOCK();
-
- s_robotInstanceCounter++;
-}
-
-JNIEXPORT void JNICALL
-Java_sun_awt_X11_XRobotPeer__1dispose (JNIEnv * env, jclass cls)
-{
- if (--s_robotInstanceCounter) {
- return;
- }
-
- // This is the last instance of the XRobotPeer being released
-
- if (xcompositeExtTested && xcompositeExtAvailable && xcompositeLibHandle) {
- // The lib is loaded in IsXCompositeAvailable(). Unload under AWT_LOCK
- // so that the shutdown function of the lib behaves correctly.
- AWT_LOCK();
- dlclose(xcompositeLibHandle);
- AWT_UNLOCK();
- }
-
- xcompositeExtTested = False;
- xcompositeExtAvailable = False;
- xcompositeLibHandle = NULL;
}
-/*
- * Returns True only if XCOMPOSITE is of version 0.3 or higher.
- * The functions that we need are available since that version.
- *
- * Must be invoked under AWT_LOCK.
- *
- * Leaves the library loaded if the version is correct.
- */
-static Bool IsXCompositeAvailable()
-{
- if (!xcompositeExtTested) {
- int opcode, eventb, errorb;
-
- if (XQueryExtension(awt_display, "Composite", &opcode, &eventb, &errorb)) {
- xcompositeLibHandle = dlopen("libXcomposite.so.1", RTLD_LAZY | RTLD_GLOBAL);
-#ifndef __linux__ /* SOLARIS */
- if (xcompositeLibHandle == NULL) {
- xcompositeLibHandle = dlopen("/usr/sfw/lib/libXcomposite.so.1",
- RTLD_LAZY | RTLD_GLOBAL);
- }
-#endif
-
- if (xcompositeLibHandle) {
- int major, minor;
- XCompositeQueryVersion = (T_XCompositeQueryVersion)dlsym(xcompositeLibHandle, "XCompositeQueryVersion");
-
- if (XCompositeQueryVersion && XCompositeQueryVersion(awt_display, &major, &minor)) {
- if (major >= 0 && minor >= 3) {
- XCompositeGetOverlayWindow = (T_XCompositeGetOverlayWindow)dlsym(xcompositeLibHandle, "XCompositeGetOverlayWindow");
- XCompositeReleaseOverlayWindow = (T_XCompositeReleaseOverlayWindow)dlsym(xcompositeLibHandle, "XCompositeReleaseOverlayWindow");
-
- if (XCompositeGetOverlayWindow && XCompositeReleaseOverlayWindow) {
- xcompositeExtAvailable = True;
- }
- }
- }
-
- if (!xcompositeExtAvailable) {
- dlclose(xcompositeLibHandle);
- } /* else the lib is unloaded in _dispose() */
- }
- }
-
- xcompositeExtTested = True;
- }
-
- return xcompositeExtAvailable;
-}
JNIEXPORT void JNICALL
Java_sun_awt_X11_XRobotPeer_getRGBPixelsImpl( JNIEnv *env,
@@ -299,7 +211,7 @@
jint *ary; /* Array of jints for sending pixel values back
* to parent process.
*/
- Window window;
+ Window rootWindow;
AwtGraphicsConfigDataPtr adata;
DTRACE_PRINTLN6("RobotPeer: getRGBPixelsImpl(%lx, %d, %d, %d, %d, %x)", xgc, x, y, width, height, pixelArray);
@@ -316,24 +228,14 @@
adata = (AwtGraphicsConfigDataPtr) JNU_GetLongFieldAsPtr(env, xgc, x11GraphicsConfigIDs.aData);
DASSERT(adata != NULL);
- window = XRootWindow(awt_display, adata->awt_visInfo.screen);
-
- if (IsXCompositeAvailable()) {
- // Use 'composite overlay window' instead of the root window.
- // See 6903034 for details.
- window = XCompositeGetOverlayWindow(awt_display, window);
- }
-
- image = getWindowImage(awt_display, window, x, y, width, height);
+ rootWindow = XRootWindow(awt_display, adata->awt_visInfo.screen);
+ image = getWindowImage(awt_display, rootWindow, x, y, width, height);
/* Array to use to crunch around the pixel values */
ary = (jint *) malloc(width * height * sizeof (jint));
if (ary == NULL) {
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
XDestroyImage(image);
- if (IsXCompositeAvailable()) {
- XCompositeReleaseOverlayWindow(awt_display, window);
- }
AWT_UNLOCK();
return;
}
@@ -354,9 +256,6 @@
free(ary);
XDestroyImage(image);
- if (IsXCompositeAvailable()) {
- XCompositeReleaseOverlayWindow(awt_display, window);
- }
AWT_UNLOCK();
}
--- a/jdk/src/windows/classes/sun/awt/windows/WFramePeer.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/windows/classes/sun/awt/windows/WFramePeer.java Tue May 24 14:32:10 2011 -0700
@@ -107,8 +107,16 @@
Rectangle currentDevBounds = currentDevGC.getBounds();
Rectangle primaryDevBounds = primaryDevGC.getBounds();
- b.width -= (currentDevBounds.width - primaryDevBounds.width);
- b.height -= (currentDevBounds.height - primaryDevBounds.height);
+ boolean isCurrentDevLarger =
+ ((currentDevBounds.width - primaryDevBounds.width > 0) ||
+ (currentDevBounds.height - primaryDevBounds.height > 0));
+
+ // the window manager doesn't seem to compensate for differences when
+ // the primary monitor is larger than the monitor that display the window
+ if (isCurrentDevLarger) {
+ b.width -= (currentDevBounds.width - primaryDevBounds.width);
+ b.height -= (currentDevBounds.height - primaryDevBounds.height);
+ }
}
}
--- a/jdk/src/windows/classes/sun/java2d/d3d/D3DRenderer.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/windows/classes/sun/java2d/d3d/D3DRenderer.java Tue May 24 14:32:10 2011 -0700
@@ -102,15 +102,20 @@
final ParallelogramPipe realpipe = d3dr.getAAParallelogramPipe();
return new ParallelogramPipe() {
public void fillParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
{
GraphicsPrimitive.tracePrimitive("D3DFillAAParallelogram");
realpipe.fillParallelogram(sg2d,
+ ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2);
}
public void drawParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
@@ -118,6 +123,7 @@
{
GraphicsPrimitive.tracePrimitive("D3DDrawAAParallelogram");
realpipe.drawParallelogram(sg2d,
+ ux1, uy1, ux2, uy2,
x, y, dx1, dy1, dx2, dy2,
lw1, lw2);
}
@@ -167,21 +173,29 @@
d3dr.fillSpans(sg2d, si, transx, transy);
}
public void fillParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2)
{
GraphicsPrimitive.tracePrimitive("D3DFillParallelogram");
- d3dr.fillParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2);
+ d3dr.fillParallelogram(sg2d,
+ ux1, uy1, ux2, uy2,
+ x, y, dx1, dy1, dx2, dy2);
}
public void drawParallelogram(SunGraphics2D sg2d,
+ double ux1, double uy1,
+ double ux2, double uy2,
double x, double y,
double dx1, double dy1,
double dx2, double dy2,
double lw1, double lw2)
{
GraphicsPrimitive.tracePrimitive("D3DDrawParallelogram");
- d3dr.drawParallelogram(sg2d, x, y, dx1, dy1, dx2, dy2, lw1, lw2);
+ d3dr.drawParallelogram(sg2d,
+ ux1, uy1, ux2, uy2,
+ x, y, dx1, dy1, dx2, dy2, lw1, lw2);
}
public void copyArea(SunGraphics2D sg2d,
int x, int y, int w, int h, int dx, int dy)
--- a/jdk/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/windows/native/sun/java2d/d3d/D3DPipelineManager.cpp Tue May 24 14:32:10 2011 -0700
@@ -192,6 +192,14 @@
pMgr = D3DPipelineManager::GetInstance();
RETURN_IF_NULL(pMgr);
hMon = pMgr->pd3d9->GetAdapterMonitor(adapter);
+
+ /*
+ * If we don't have devices initialized yet, no sense to clear them.
+ */
+ if (!Devices::GetInstance()){
+ return;
+ }
+
gdiScreen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(hMon);
JNU_CallStaticMethodByName(env, NULL,
--- a/jdk/src/windows/native/sun/windows/Devices.h Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/windows/native/sun/windows/Devices.h Tue May 24 14:32:10 2011 -0700
@@ -36,6 +36,7 @@
class Devices {
public:
+static Devices* GetInstance();
static BOOL UpdateInstance(JNIEnv *env);
int GetNumDevices() { return numDevices; }
AwtWin32GraphicsDevice* GetDeviceReference(int index, BOOL adjust = TRUE);
@@ -59,7 +60,6 @@
private:
Devices(int numElements);
void AddReference();
-static Devices* GetInstance();
AwtWin32GraphicsDevice** devices;
int refCount;
--- a/jdk/src/windows/native/sun/windows/awt_Choice.cpp Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/windows/native/sun/windows/awt_Choice.cpp Tue May 24 14:32:10 2011 -0700
@@ -396,6 +396,12 @@
DASSERT(::IsWindow(hwnd));
+ // This branch is required for the proper work of AwtComponent::GetComponent() method
+ // while hovering drop-down list
+ if (message == WmAwtIsComponent) {
+ return (LRESULT)TRUE;
+ }
+
switch (message) {
case WM_LBUTTONDOWN: {
DWORD curPos = ::GetMessagePos();
--- a/jdk/src/windows/native/sun/windows/awt_Component.cpp Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/windows/native/sun/windows/awt_Component.cpp Tue May 24 14:32:10 2011 -0700
@@ -364,7 +364,6 @@
AwtComponent *component =
(AwtComponent *)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
DASSERT(!component || !IsBadReadPtr(component, sizeof(AwtComponent)) );
- DASSERT(!component || component->GetHWnd() == hWnd );
return component;
}
--- a/jdk/src/windows/native/sun/windows/awt_Frame.cpp Tue May 24 14:15:14 2011 -0700
+++ b/jdk/src/windows/native/sun/windows/awt_Frame.cpp Tue May 24 14:32:10 2011 -0700
@@ -344,17 +344,6 @@
SetImeTargetComponent(NULL);
}
break;
- // TODO: when a Choice's list is dropped down and we're scrolling in
- // the list WM_MOUSEWHEEL messages come to the poxy, not to the list. Why?
- case WM_MOUSEWHEEL:
- focusOwner = AwtComponent::GetComponent(sm_focusOwner);
- if (focusOwner != NULL &&
- focusOwner != this) // avoid recursive calls
- {
- retValue = focusOwner->WindowProc(message, wParam, lParam);
- mr = mrConsume;
- }
- break;
case WM_SETFOCUS:
if (sm_inSynthesizeFocus) break; // pass it up the WindowProc chain
--- a/jdk/test/java/awt/Component/Revalidate/Revalidate.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/test/java/awt/Component/Revalidate/Revalidate.java Tue May 24 14:32:10 2011 -0700
@@ -26,7 +26,7 @@
@bug 7036669
@summary Test Component.revalidate() method
@author anthony.petrov@oracle.com: area=awt.component
- @run main Revalidate
+ @run main/othervm -Djava.awt.smartInvalidate=true Revalidate
*/
import java.awt.*;
--- a/jdk/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java Tue May 24 14:32:10 2011 -0700
@@ -26,7 +26,7 @@
@bug 6852592
@summary invalidate() must stop when it encounters a validate root
@author anthony.petrov@sun.com
- @run main InvalidateMustRespectValidateRoots
+ @run main/othervm -Djava.awt.smartInvalidate=true InvalidateMustRespectValidateRoots
*/
import javax.swing.*;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Paint/PgramUserBoundsTest.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 7043054
+ * @summary Verifies that Paint objects receive the appropriate user space
+ * bounds in their createContext() method
+ * @run main PgramUserBoundsTest
+ */
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.PaintContext;
+import java.awt.RenderingHints;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+
+public class PgramUserBoundsTest {
+ static final int MinX = 10;
+ static final int MinY = 20;
+ static final int MaxX = 30;
+ static final int MaxY = 50;
+ static AffineTransform identity = new AffineTransform();
+
+ public static void main(String argv[]) {
+ BufferedImage bimg =
+ new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
+ Graphics2D g2d = bimg.createGraphics();
+ g2d.setPaint(new BoundsCheckerPaint(MinX, MinY, MaxX, MaxY));
+ testAll(g2d);
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ testAll(g2d);
+ }
+
+ static void testAll(Graphics2D g2d) {
+ g2d.setTransform(identity);
+ g2d.translate(100, 100);
+ testPrimitives(g2d);
+
+ g2d.setTransform(identity);
+ g2d.scale(10, 10);
+ testPrimitives(g2d);
+
+ g2d.setTransform(identity);
+ g2d.rotate(Math.PI/6);
+ testPrimitives(g2d);
+ }
+
+ static void testPrimitives(Graphics2D g2d) {
+ testLine(g2d);
+ testRect(g2d);
+ }
+
+ static void testLine(Graphics2D g2d) {
+ testLine(g2d, MinX, MinY, MaxX, MaxY);
+ testLine(g2d, MaxX, MinY, MinX, MaxY);
+ testLine(g2d, MinX, MaxY, MaxX, MinY);
+ testLine(g2d, MaxX, MaxY, MinX, MinY);
+ }
+
+ static void testRect(Graphics2D g2d) {
+ g2d.fillRect(MinX, MinY, MaxX - MinX, MaxY - MinY);
+ g2d.fill(new Rectangle(MinX, MinY, MaxX - MinX, MaxY - MinY));
+ }
+
+ static void testLine(Graphics2D g2d, int x1, int y1, int x2, int y2) {
+ g2d.drawLine(x1, y1, x2, y2);
+ g2d.draw(new Line2D.Double(x1, y1, x2, y2));
+ }
+
+ static class BoundsCheckerPaint implements Paint {
+ private Color c = Color.WHITE;
+ private Rectangle2D expectedBounds;
+
+ public BoundsCheckerPaint(double x1, double y1,
+ double x2, double y2)
+ {
+ expectedBounds = new Rectangle2D.Double();
+ expectedBounds.setFrameFromDiagonal(x1, y1, x2, y2);
+ }
+
+ public int getTransparency() {
+ return c.getTransparency();
+ }
+
+ public PaintContext createContext(ColorModel cm,
+ Rectangle deviceBounds,
+ Rectangle2D userBounds,
+ AffineTransform xform,
+ RenderingHints hints)
+ {
+ System.out.println("user bounds = "+userBounds);
+ if (!userBounds.equals(expectedBounds)) {
+ throw new RuntimeException("bounds fail to match");
+ }
+ return c.createContext(cm, deviceBounds, userBounds, xform, hints);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Toolkit/Headless/ExceptionContract/ExceptionContract.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ @test
+ @bug 7040577
+ @library ../../../regtesthelpers
+ @build Sysout
+ @summary Default implementation of Toolkit.loadSystemColors(int[]) and many others doesn't throw HE in hl env
+ @author andrei dmitriev: area=awt.headless
+ @run main/othervm -Djava.awt.headless=true ExceptionContract
+*/
+
+import java.awt.*;
+import java.util.Properties;
+import test.java.awt.regtesthelpers.Sysout;
+
+import java.awt.datatransfer.Clipboard;
+import java.awt.dnd.*;
+import java.awt.dnd.peer.DragSourceContextPeer;
+import java.awt.font.TextAttribute;
+import java.awt.im.InputMethodHighlight;
+import java.awt.image.*;
+import java.awt.peer.*;
+import java.net.URL;
+import java.util.Map;
+import java.util.Properties;
+
+public class ExceptionContract {
+
+ private static boolean passed = false;
+ public static void main(String[] args) {
+ //Case1
+ try{
+ new _Toolkit().getLockingKeyState(1);
+ } catch (HeadlessException he){
+ passed = true;
+ }
+ if (!passed){
+ throw new RuntimeException("Tk.getLockingKeyState() didn't throw HeadlessException while in the headless mode.");
+ }
+
+ passed = false;
+ //Case2
+ try{
+ new _Toolkit().setLockingKeyState(1, true);
+ } catch (HeadlessException he){
+ passed = true;
+ }
+ if (!passed){
+ throw new RuntimeException("Tk.setLockingKeyState() didn't throw HeadlessException while in the headless mode.");
+ }
+
+ passed = false;
+ //Case3
+ try{
+ new _Toolkit().createCustomCursor(new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB), new Point(0,0), "Custom cursor");
+ } catch (HeadlessException he){
+ he.printStackTrace();
+ passed = true;
+ }
+ if (!passed){
+ throw new RuntimeException("Tk.createCustomCursor(args) didn't throw HeadlessException while in the headless mode.");
+ }
+
+ }
+
+ static class _Toolkit extends Toolkit {
+
+ @Override
+ public Cursor createCustomCursor(Image cursor, Point hotSpot, String name)
+ throws IndexOutOfBoundsException, HeadlessException
+ {
+ return super.createCustomCursor(cursor, hotSpot, name);
+ }
+
+
+ @Override
+ public void setLockingKeyState(int keyCode, boolean on) throws UnsupportedOperationException {
+ super.setLockingKeyState(keyCode, on);
+ }
+
+ @Override
+ public boolean getLockingKeyState(int keyCode) throws UnsupportedOperationException {
+ return super.getLockingKeyState(keyCode);
+ }
+
+
+ @Override
+ public void loadSystemColors(int[] systemColors) throws HeadlessException {
+ return;
+ }
+
+ @Override
+ protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected ButtonPeer createButton(Button target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected TextFieldPeer createTextField(TextField target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected LabelPeer createLabel(Label target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected ListPeer createList(List target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected CheckboxPeer createCheckbox(Checkbox target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected ScrollbarPeer createScrollbar(Scrollbar target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected ScrollPanePeer createScrollPane(ScrollPane target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected TextAreaPeer createTextArea(TextArea target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected ChoicePeer createChoice(Choice target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected FramePeer createFrame(Frame target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected CanvasPeer createCanvas(Canvas target) {
+ return null;
+ }
+
+ @Override
+ protected PanelPeer createPanel(Panel target) {
+ return null;
+ }
+
+ @Override
+ protected WindowPeer createWindow(Window target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected DialogPeer createDialog(Dialog target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected MenuBarPeer createMenuBar(MenuBar target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected MenuPeer createMenu(Menu target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected PopupMenuPeer createPopupMenu(PopupMenu target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected MenuItemPeer createMenuItem(MenuItem target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected FileDialogPeer createFileDialog(FileDialog target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected FontPeer getFontPeer(String name, int style) {
+ return null;
+ }
+
+ @Override
+ public Dimension getScreenSize() throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ public int getScreenResolution() throws HeadlessException {
+ return 0;
+ }
+
+ @Override
+ public ColorModel getColorModel() throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ public String[] getFontList() {
+ return new String[0];
+ }
+
+ @Override
+ public FontMetrics getFontMetrics(Font font) {
+ return null;
+ }
+
+ @Override
+ public void sync() {
+
+ }
+
+ @Override
+ public Image getImage(String filename) {
+ return null;
+ }
+
+ @Override
+ public Image getImage(URL url) {
+ return null;
+ }
+
+ @Override
+ public Image createImage(String filename) {
+ return null;
+ }
+
+ @Override
+ public Image createImage(URL url) {
+ return null;
+ }
+
+ @Override
+ public boolean prepareImage(Image image, int width, int height, ImageObserver observer) {
+ return false;
+ }
+
+ @Override
+ public int checkImage(Image image, int width, int height, ImageObserver observer) {
+ return 0;
+ }
+
+ @Override
+ public Image createImage(ImageProducer producer) {
+ return null;
+ }
+
+ @Override
+ public Image createImage(byte[] imagedata, int imageoffset, int imagelength) {
+ return null;
+ }
+
+ @Override
+ public PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
+ return null;
+ }
+
+ @Override
+ public void beep() {
+
+ }
+
+ @Override
+ public Clipboard getSystemClipboard() throws HeadlessException {
+ return null;
+ }
+
+ @Override
+ protected EventQueue getSystemEventQueueImpl() {
+ return null;
+ }
+
+ @Override
+ public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
+ return null;
+ }
+
+ @Override
+ public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) {
+ return false;
+ }
+
+ @Override
+ public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType modalExclusionType) {
+ return false;
+ }
+
+ @Override
+ public Map<TextAttribute, ?> mapInputMethodHighlight(InputMethodHighlight highlight) throws HeadlessException {
+ return null;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/geom/Arc2D/SerializationTest.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 7040717 6522514
+ * @summary Verifies that subclasses of Arc2D can be serialized.
+ * @run main SerializationTest
+ */
+
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.Arc2D;
+import java.io.Serializable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+public class SerializationTest {
+ static boolean failed;
+ public static void main(String args[]) {
+ test(new Arc());
+ test(new ArcF());
+ test(new ArcD());
+ if (failed) throw new RuntimeException("some tests failed");
+ }
+
+ public static void test(Object a) {
+ try {
+ File objectbin = new File("object.bin");
+ FileOutputStream fos = new FileOutputStream(objectbin);
+ ObjectOutputStream out = new ObjectOutputStream(fos);
+ out.writeObject(a);
+ fos.close();
+ FileInputStream fis = new FileInputStream(objectbin);
+ ObjectInputStream in = new ObjectInputStream(fis);
+ Object o = in.readObject();
+ fis.close();
+ System.err.println(o);
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ failed = true;
+ }
+ }
+
+ static class Arc extends Arc2D implements Serializable {
+ public Arc() {
+ super(Arc2D.OPEN);
+ }
+
+ public Rectangle2D makeBounds(double x, double y, double w, double h) {
+ return new Rectangle2D.Double(x, y, w, h);
+ }
+ public double getX() { return 0; }
+ public double getY() { return 0; }
+ public double getWidth() { return 0; }
+ public double getHeight() { return 0; }
+ public double getAngleExtent() { return 0; }
+ public double getAngleStart() { return 0; }
+ public void setAngleExtent(double angExt) { }
+ public void setAngleStart(double angExt) { }
+ public void setFrame(double x, double y, double w, double h) {}
+ public void setArc(double x, double y, double w, double h,
+ double s, double e, int c)
+ {
+ }
+ public boolean isEmpty() { return false; };
+ }
+
+ static class ArcF extends Arc2D.Float implements Serializable {
+ public ArcF() {
+ }
+ }
+
+ static class ArcD extends Arc2D.Double implements Serializable {
+ public ArcD() {
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/6998541/Test6998541.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @bug 6998541
+ * @summary JSR 292 implement missing return-type conversion for OP_RETYPE_RAW
+ *
+ * @run main/othervm -Xbatch
+ * -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2
+ * -DTest6998541.N=100000 -DTest6998541.KIND=cast Test6998541
+ * @run main/othervm -Xbatch
+ * -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2
+ * -DTest6998541.N=100000 -DTest6998541.KIND=normal Test6998541
+ */
+
+import java.util.*;
+
+import java.lang.invoke.*;
+import static java.lang.invoke.MethodHandles.*;
+
+public class Test6998541 {
+ private static final Class CLASS = Test6998541.class;
+ private static final String NAME = "identity";
+ private static final int N = Math.max(2, Integer.getInteger(CLASS.getSimpleName()+".N", 10000));
+ private static final String KIND = System.getProperty(CLASS.getSimpleName()+".KIND", "cast");
+ private static final int BITS = 0x00000201;
+
+ private static final boolean DO_CASTS = !KIND.equals("normal");
+
+ public static void main(String[] args) throws Throwable {
+ System.out.println("KIND="+KIND+" DO_CASTS="+DO_CASTS+" N="+N);
+ doboolean();
+ dobyte();
+ dochar();
+ doshort();
+ doint();
+ dolong();
+ dofloat();
+ dodouble();
+ dovoid();
+ }
+
+ private static void doboolean() throws Throwable {
+ for (int i = 0; i < N; i++) {
+ boolean2prim(false);
+ boolean2prim(true);
+ }
+ boolean2prim_invalid(true);
+ }
+ private static void dobyte() throws Throwable {
+ byte x = Byte.MIN_VALUE;
+ for (int i = 0; i < N; i++, x++)
+ byte2prim(x);
+ byte2prim_invalid(x);
+ }
+ private static void dochar() throws Throwable {
+ char x = Character.MIN_VALUE;
+ for (int i = 0; i < N; i++, x++)
+ char2prim(x);
+ char2prim_invalid(x);
+ }
+ private static void doshort() throws Throwable {
+ short x = Short.MIN_VALUE;
+ for (int i = 0; i < N; i++, x++)
+ short2prim(x);
+ short2prim_invalid(x);
+ }
+ private static void doint() throws Throwable {
+ int x = Integer.MIN_VALUE;
+ int D = Integer.MAX_VALUE / (N / 2) | BITS;
+ for (int i = 0; i < N; i++, x += D) {
+ int2prim(x);
+ }
+ int2prim_invalid(x);
+ }
+ private static void dolong() throws Throwable {
+ long x = Long.MIN_VALUE;
+ long D = Long.MAX_VALUE / ((long) (N / 2)) | BITS;
+ for (int i = 0; i < N; i++, x += D)
+ long2prim(x);
+ long2prim_invalid(x);
+ }
+ private static void dofloat() throws Throwable {
+ float x = Float.MIN_VALUE;
+ float D = Float.MAX_VALUE / ((float) (N / 2));
+ for (int i = 0; i < N; i++, x += D)
+ float2prim(x);
+ float2prim_invalid(x);
+ }
+ private static void dodouble() throws Throwable {
+ double x = Double.MIN_VALUE;
+ double D = Double.MAX_VALUE / ((double) (N / 2));
+ for (int i = 0; i < N; i++, x += D)
+ double2prim(x);
+ double2prim_invalid(x);
+ }
+ private static void dovoid() throws Throwable {
+ for (int i = 0; i < N; i++) {
+ void2prim(i);
+ }
+ void2prim_invalid(0);
+ // do the other direction here also:
+ for (int i = 0; i < N; i++) {
+ prim2void(i);
+ }
+ prim2void_invalid(0);
+ }
+
+ private static void assertEquals(Object o, Object o2) {
+ if (!o.equals(o2))
+ throw new AssertionError("expected: " + o + ", found: " + o2);
+ }
+ private static void fail() {
+ throw new AssertionError();
+ }
+
+ private final static MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ private static MethodHandle mh(Class ret, Class... args) {
+ try {
+ MethodType mt = MethodType.methodType(ret, args);
+ Class lookupRet = (args.length == 0 ? void.class : args[0]);
+ MethodHandle mh = lookup.findStatic(CLASS, NAME, mt.changeReturnType(lookupRet));
+ if (DO_CASTS)
+ return MethodHandles.explicitCastArguments(mh, mt);
+ if (canDoAsType(mh.type(), mt))
+ return mh.asType(mt);
+ try {
+ mh.asType(mt);
+ throw new AssertionError("asType should not succeed: "+mh+" => "+mt);
+ } catch (WrongMethodTypeException ex) {
+ // this was a required WMTE
+ return mh.asType(mt.generic()).asType(mt);
+ }
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ private static final Class<?>[] NUMERIC_TYPE_WIDENING_ORDER = {
+ byte.class, short.class, int.class, long.class, float.class, double.class
+ };
+ private static boolean canDoAsType(Class<?> src, Class<?> dst) {
+ if (src == dst) return true;
+ if (dst == void.class) return true;
+ if (!src.isPrimitive() || !dst.isPrimitive()) return true;
+ // primitive conversion works for asType only when it's widening
+ if (src == boolean.class || dst == boolean.class) return false;
+ if (dst == char.class) return false;
+ if (src == char.class) src = int.class; // can widen char to int
+ for (Class<?> ntype : NUMERIC_TYPE_WIDENING_ORDER) {
+ if (src == ntype) return true;
+ if (dst == ntype) return false;
+ }
+ throw new AssertionError("should not reach here: "+src+", "+dst);
+ }
+ private static boolean canDoAsType(MethodType mt0, MethodType mt1) {
+ Class<?> rt0 = mt0.returnType();
+ Class<?> rt1 = mt1.returnType();
+ if (!canDoAsType(rt0, rt1)) return false;
+ int argc = mt0.parameterCount();
+ if (argc != mt1.parameterCount()) return false;
+ for (int i = 0; i < argc; i++) {
+ if (!canDoAsType(mt1.parameterType(i), mt0.parameterType(i)))
+ return false;
+ }
+ return true;
+ }
+
+ private static MethodHandle mh_z(Class ret) { return mh(ret, boolean.class); }
+
+ private final static MethodHandle mh_zz = mh_z(boolean.class);
+ private final static MethodHandle mh_bz = mh_z(byte.class );
+ private final static MethodHandle mh_cz = mh_z(char.class );
+ private final static MethodHandle mh_sz = mh_z(short.class );
+ private final static MethodHandle mh_iz = mh_z(int.class );
+ private final static MethodHandle mh_jz = mh_z(long.class );
+ private final static MethodHandle mh_fz = mh_z(float.class );
+ private final static MethodHandle mh_dz = mh_z(double.class );
+
+ private static void boolean2prim(boolean x) throws Throwable {
+ int i = x ? 1 : 0;
+ assertEquals( x, (boolean) mh_zz.invokeExact(x)); // boolean -> boolean
+ if (!DO_CASTS) return;
+ assertEquals((byte) i, (byte) mh_bz.invokeExact(x)); // boolean -> byte
+ assertEquals((char) i, (char) mh_cz.invokeExact(x)); // boolean -> char
+ assertEquals((short) i, (short) mh_sz.invokeExact(x)); // boolean -> short
+ assertEquals((int) i, (int) mh_iz.invokeExact(x)); // boolean -> int
+ assertEquals((long) i, (long) mh_jz.invokeExact(x)); // boolean -> long
+ assertEquals((float) i, (float) mh_fz.invokeExact(x)); // boolean -> float
+ assertEquals((double) i, (double) mh_dz.invokeExact(x)); // boolean -> double
+ }
+ private static void boolean2prim_invalid(boolean x) throws Throwable {
+ if (DO_CASTS) return;
+ try { byte y = (byte) mh_bz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> byte
+ try { char y = (char) mh_cz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> char
+ try { short y = (short) mh_sz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> short
+ try { int y = (int) mh_iz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> int
+ try { long y = (long) mh_jz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> long
+ try { float y = (float) mh_fz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> float
+ try { double y = (double) mh_dz.invokeExact(x); fail(); } catch (ClassCastException _) {} // boolean -> double
+ }
+
+ private static MethodHandle mh_b(Class ret) { return mh(ret, byte.class); }
+
+ private final static MethodHandle mh_zb = mh_b(boolean.class);
+ private final static MethodHandle mh_bb = mh_b(byte.class );
+ private final static MethodHandle mh_cb = mh_b(char.class );
+ private final static MethodHandle mh_sb = mh_b(short.class );
+ private final static MethodHandle mh_ib = mh_b(int.class );
+ private final static MethodHandle mh_jb = mh_b(long.class );
+ private final static MethodHandle mh_fb = mh_b(float.class );
+ private final static MethodHandle mh_db = mh_b(double.class );
+
+ private static void byte2prim(byte x) throws Throwable {
+ assertEquals((byte) x, (byte) mh_bb.invokeExact(x)); // byte -> byte
+ assertEquals((short) x, (short) mh_sb.invokeExact(x)); // byte -> short
+ assertEquals((int) x, (int) mh_ib.invokeExact(x)); // byte -> int
+ assertEquals((long) x, (long) mh_jb.invokeExact(x)); // byte -> long
+ assertEquals((float) x, (float) mh_fb.invokeExact(x)); // byte -> float
+ assertEquals((double) x, (double) mh_db.invokeExact(x)); // byte -> double
+ if (!DO_CASTS) return;
+ boolean z = ((x & 1) != 0);
+ assertEquals((char) x, (char) mh_cb.invokeExact(x)); // byte -> char
+ assertEquals((boolean) z, (boolean) mh_zb.invokeExact(x)); // byte -> boolean
+ }
+ private static void byte2prim_invalid(byte x) throws Throwable {
+ if (DO_CASTS) return;
+ try { char y = (char) mh_cb.invokeExact(x); fail(); } catch (ClassCastException _) {} // byte -> char
+ try { boolean y = (boolean) mh_zb.invokeExact(x); fail(); } catch (ClassCastException _) {} // byte -> boolean
+ }
+
+ private static MethodHandle mh_c(Class ret) { return mh(ret, char.class); }
+
+ private final static MethodHandle mh_zc = mh_c(boolean.class);
+ private final static MethodHandle mh_bc = mh_c(byte.class );
+ private final static MethodHandle mh_cc = mh_c(char.class );
+ private final static MethodHandle mh_sc = mh_c(short.class );
+ private final static MethodHandle mh_ic = mh_c(int.class );
+ private final static MethodHandle mh_jc = mh_c(long.class );
+ private final static MethodHandle mh_fc = mh_c(float.class );
+ private final static MethodHandle mh_dc = mh_c(double.class );
+
+ private static void char2prim(char x) throws Throwable {
+ assertEquals((char) x, (char) mh_cc.invokeExact(x)); // char -> char
+ assertEquals((int) x, (int) mh_ic.invokeExact(x)); // char -> int
+ assertEquals((long) x, (long) mh_jc.invokeExact(x)); // char -> long
+ assertEquals((float) x, (float) mh_fc.invokeExact(x)); // char -> float
+ assertEquals((double) x, (double) mh_dc.invokeExact(x)); // char -> double
+ if (!DO_CASTS) return;
+ boolean z = ((x & 1) != 0);
+ assertEquals((boolean) z, (boolean) mh_zc.invokeExact(x)); // char -> boolean
+ assertEquals((byte) x, (byte) mh_bc.invokeExact(x)); // char -> byte
+ assertEquals((short) x, (short) mh_sc.invokeExact(x)); // char -> short
+ }
+ private static void char2prim_invalid(char x) throws Throwable {
+ if (DO_CASTS) return;
+ try { boolean y = (boolean) mh_zc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> boolean
+ try { byte y = (byte) mh_bc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> byte
+ try { short y = (short) mh_sc.invokeExact(x); fail(); } catch (ClassCastException _) {} // char -> short
+ }
+
+ private static MethodHandle mh_s(Class ret) { return mh(ret, short.class); }
+
+ private final static MethodHandle mh_zs = mh_s(boolean.class);
+ private final static MethodHandle mh_bs = mh_s(byte.class );
+ private final static MethodHandle mh_cs = mh_s(char.class );
+ private final static MethodHandle mh_ss = mh_s(short.class );
+ private final static MethodHandle mh_is = mh_s(int.class );
+ private final static MethodHandle mh_js = mh_s(long.class );
+ private final static MethodHandle mh_fs = mh_s(float.class );
+ private final static MethodHandle mh_ds = mh_s(double.class );
+
+ private static void short2prim(short x) throws Throwable {
+ assertEquals((short) x, (short) mh_ss.invokeExact(x)); // short -> short
+ assertEquals((int) x, (int) mh_is.invokeExact(x)); // short -> int
+ assertEquals((long) x, (long) mh_js.invokeExact(x)); // short -> long
+ assertEquals((float) x, (float) mh_fs.invokeExact(x)); // short -> float
+ assertEquals((double) x, (double) mh_ds.invokeExact(x)); // short -> double
+ if (!DO_CASTS) return;
+ boolean z = ((x & 1) != 0);
+ assertEquals((boolean) z, (boolean) mh_zs.invokeExact(x)); // short -> boolean
+ assertEquals((byte) x, (byte) mh_bs.invokeExact(x)); // short -> byte
+ assertEquals((char) x, (char) mh_cs.invokeExact(x)); // short -> char
+ }
+ private static void short2prim_invalid(short x) throws Throwable {
+ if (DO_CASTS) return;
+ try { boolean y = (boolean) mh_zs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> boolean
+ try { byte y = (byte) mh_bs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> byte
+ try { char y = (char) mh_cs.invokeExact(x); fail(); } catch (ClassCastException _) {} // short -> char
+ }
+
+ private static MethodHandle mh_i(Class ret) { return mh(ret, int.class); }
+
+ private final static MethodHandle mh_zi = mh_i(boolean.class);
+ private final static MethodHandle mh_bi = mh_i(byte.class );
+ private final static MethodHandle mh_ci = mh_i(char.class );
+ private final static MethodHandle mh_si = mh_i(short.class );
+ private final static MethodHandle mh_ii = mh_i(int.class );
+ private final static MethodHandle mh_ji = mh_i(long.class );
+ private final static MethodHandle mh_fi = mh_i(float.class );
+ private final static MethodHandle mh_di = mh_i(double.class );
+
+ private static void int2prim(int x) throws Throwable {
+ assertEquals((int) x, (int) mh_ii.invokeExact(x)); // int -> int
+ assertEquals((long) x, (long) mh_ji.invokeExact(x)); // int -> long
+ assertEquals((float) x, (float) mh_fi.invokeExact(x)); // int -> float
+ assertEquals((double) x, (double) mh_di.invokeExact(x)); // int -> double
+ if (!DO_CASTS) return;
+ boolean z = ((x & 1) != 0);
+ assertEquals((boolean) z, (boolean) mh_zi.invokeExact(x)); // int -> boolean
+ assertEquals((byte) x, (byte) mh_bi.invokeExact(x)); // int -> byte
+ assertEquals((char) x, (char) mh_ci.invokeExact(x)); // int -> char
+ assertEquals((short) x, (short) mh_si.invokeExact(x)); // int -> short
+ }
+ private static void int2prim_invalid(int x) throws Throwable {
+ if (DO_CASTS) return;
+ try { boolean y = (boolean) mh_zi.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> boolean
+ try { byte y = (byte) mh_bi.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> byte
+ try { char y = (char) mh_ci.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> char
+ try { short y = (short) mh_si.invokeExact(x); fail(); } catch (ClassCastException _) {} // int -> short
+ }
+
+ private static MethodHandle mh_j(Class ret) { return mh(ret, long.class); }
+
+ private final static MethodHandle mh_zj = mh_j(boolean.class);
+ private final static MethodHandle mh_bj = mh_j(byte.class );
+ private final static MethodHandle mh_cj = mh_j(char.class );
+ private final static MethodHandle mh_sj = mh_j(short.class );
+ private final static MethodHandle mh_ij = mh_j(int.class );
+ private final static MethodHandle mh_jj = mh_j(long.class );
+ private final static MethodHandle mh_fj = mh_j(float.class );
+ private final static MethodHandle mh_dj = mh_j(double.class );
+
+ private static void long2prim(long x) throws Throwable {
+ assertEquals((long) x, (long) mh_jj.invokeExact(x)); // long -> long
+ assertEquals((float) x, (float) mh_fj.invokeExact(x)); // long -> float
+ assertEquals((double) x, (double) mh_dj.invokeExact(x)); // long -> double
+ if (!DO_CASTS) return;
+ boolean z = ((x & 1) != 0);
+ assertEquals((boolean)z, (boolean) mh_zj.invokeExact(x)); // long -> boolean
+ assertEquals((byte) x, (byte) mh_bj.invokeExact(x)); // long -> byte
+ assertEquals((char) x, (char) mh_cj.invokeExact(x)); // long -> char
+ assertEquals((short) x, (short) mh_sj.invokeExact(x)); // long -> short
+ assertEquals((int) x, (int) mh_ij.invokeExact(x)); // long -> int
+ }
+ private static void long2prim_invalid(long x) throws Throwable {
+ if (DO_CASTS) return;
+ try { boolean y = (boolean) mh_zj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> boolean
+ try { byte y = (byte) mh_bj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> byte
+ try { char y = (char) mh_cj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> char
+ try { short y = (short) mh_sj.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> short
+ try { int y = (int) mh_ij.invokeExact(x); fail(); } catch (ClassCastException _) {} // long -> int
+ }
+
+ private static MethodHandle mh_f(Class ret) { return mh(ret, float.class); }
+
+ private final static MethodHandle mh_zf = mh_f(boolean.class);
+ private final static MethodHandle mh_bf = mh_f(byte.class );
+ private final static MethodHandle mh_cf = mh_f(char.class );
+ private final static MethodHandle mh_sf = mh_f(short.class );
+ private final static MethodHandle mh_if = mh_f(int.class );
+ private final static MethodHandle mh_jf = mh_f(long.class );
+ private final static MethodHandle mh_ff = mh_f(float.class );
+ private final static MethodHandle mh_df = mh_f(double.class );
+
+ private static void float2prim(float x) throws Throwable {
+ assertEquals((float) x, (float) mh_ff.invokeExact(x)); // float -> float
+ assertEquals((double) x, (double) mh_df.invokeExact(x)); // float -> double
+ if (!DO_CASTS) return;
+ boolean z = (((byte) x & 1) != 0);
+ assertEquals((boolean) z, (boolean) mh_zf.invokeExact(x)); // float -> boolean
+ assertEquals((byte) x, (byte) mh_bf.invokeExact(x)); // float -> byte
+ assertEquals((char) x, (char) mh_cf.invokeExact(x)); // float -> char
+ assertEquals((short) x, (short) mh_sf.invokeExact(x)); // float -> short
+ assertEquals((int) x, (int) mh_if.invokeExact(x)); // float -> int
+ assertEquals((long) x, (long) mh_jf.invokeExact(x)); // float -> long
+ }
+ private static void float2prim_invalid(float x) throws Throwable {
+ if (DO_CASTS) return;
+ try { boolean y = (boolean) mh_zf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> boolean
+ try { byte y = (byte) mh_bf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> byte
+ try { char y = (char) mh_cf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> char
+ try { short y = (short) mh_sf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> short
+ try { int y = (int) mh_if.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> int
+ try { long y = (long) mh_jf.invokeExact(x); fail(); } catch (ClassCastException _) {} // float -> long
+ }
+
+ private static MethodHandle mh_d(Class ret) { return mh(ret, double.class); }
+
+ private final static MethodHandle mh_zd = mh_d(boolean.class);
+ private final static MethodHandle mh_bd = mh_d(byte.class );
+ private final static MethodHandle mh_cd = mh_d(char.class );
+ private final static MethodHandle mh_sd = mh_d(short.class );
+ private final static MethodHandle mh_id = mh_d(int.class );
+ private final static MethodHandle mh_jd = mh_d(long.class );
+ private final static MethodHandle mh_fd = mh_d(float.class );
+ private final static MethodHandle mh_dd = mh_d(double.class );
+
+ private static void double2prim(double x) throws Throwable {
+ assertEquals((double) x, (double) mh_dd.invokeExact(x)); // double -> double
+ if (!DO_CASTS) return;
+ boolean z = (((byte) x & 1) != 0);
+ assertEquals((boolean) z, (boolean) mh_zd.invokeExact(x)); // double -> boolean
+ assertEquals((byte) x, (byte) mh_bd.invokeExact(x)); // double -> byte
+ assertEquals((char) x, (char) mh_cd.invokeExact(x)); // double -> char
+ assertEquals((short) x, (short) mh_sd.invokeExact(x)); // double -> short
+ assertEquals((int) x, (int) mh_id.invokeExact(x)); // double -> int
+ assertEquals((long) x, (long) mh_jd.invokeExact(x)); // double -> long
+ assertEquals((float) x, (float) mh_fd.invokeExact(x)); // double -> float
+ }
+ private static void double2prim_invalid(double x) throws Throwable {
+ if (DO_CASTS) return;
+ try { boolean y = (boolean) mh_zd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> boolean
+ try { byte y = (byte) mh_bd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> byte
+ try { char y = (char) mh_cd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> char
+ try { short y = (short) mh_sd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> short
+ try { int y = (int) mh_id.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> int
+ try { long y = (long) mh_jd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> long
+ try { float y = (float) mh_fd.invokeExact(x); fail(); } catch (ClassCastException _) {} // double -> float
+ }
+
+ private final static MethodHandle mh_zv = mh(boolean.class);
+ private final static MethodHandle mh_bv = mh(byte.class );
+ private final static MethodHandle mh_cv = mh(char.class );
+ private final static MethodHandle mh_sv = mh(short.class );
+ private final static MethodHandle mh_iv = mh(int.class );
+ private final static MethodHandle mh_jv = mh(long.class );
+ private final static MethodHandle mh_fv = mh(float.class );
+ private final static MethodHandle mh_dv = mh(double.class );
+
+ private static void void2prim(int i) throws Throwable {
+ if (!DO_CASTS) return;
+ assertEquals( false, (boolean) mh_zv.invokeExact()); // void -> boolean
+ assertEquals((byte) 0, (byte) mh_bv.invokeExact()); // void -> byte
+ assertEquals((char) 0, (char) mh_cv.invokeExact()); // void -> char
+ assertEquals((short) 0, (short) mh_sv.invokeExact()); // void -> short
+ assertEquals( 0, (int) mh_iv.invokeExact()); // void -> int
+ assertEquals( 0L, (long) mh_jv.invokeExact()); // void -> long
+ assertEquals( 0.0f, (float) mh_fv.invokeExact()); // void -> float
+ assertEquals( 0.0d, (double) mh_dv.invokeExact()); // void -> double
+ }
+
+ private static void void2prim_invalid(double x) throws Throwable {
+ if (DO_CASTS) return;
+ try { assertEquals( false, (boolean) mh_zv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> boolean
+ try { assertEquals((byte) 0, (byte) mh_bv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> byte
+ try { assertEquals((char) 0, (char) mh_cv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> char
+ try { assertEquals((short) 0, (short) mh_sv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> short
+ try { assertEquals( 0, (int) mh_iv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> int
+ try { assertEquals( 0L, (long) mh_jv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> long
+ try { assertEquals( 0.0f, (float) mh_fv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> float
+ try { assertEquals( 0.0d, (double) mh_dv.invokeExact()); fail(); } catch (NullPointerException _) {} // void -> double
+ }
+
+ private static MethodHandle mh_v(Class arg) { return mh(void.class, arg); }
+
+ private final static MethodHandle mh_vz = mh_v(boolean.class);
+ private final static MethodHandle mh_vb = mh_v(byte.class );
+ private final static MethodHandle mh_vc = mh_v(char.class );
+ private final static MethodHandle mh_vs = mh_v(short.class );
+ private final static MethodHandle mh_vi = mh_v(int.class );
+ private final static MethodHandle mh_vj = mh_v(long.class );
+ private final static MethodHandle mh_vf = mh_v(float.class );
+ private final static MethodHandle mh_vd = mh_v(double.class );
+
+ private static void prim2void(int x) throws Throwable {
+ boolean z = ((x & 1) != 0);
+ mh_vz.invokeExact( z); // boolean -> void
+ mh_vb.invokeExact((byte) x); // byte -> void
+ mh_vc.invokeExact((char) x); // char -> void
+ mh_vs.invokeExact((short) x); // short -> void
+ mh_vi.invokeExact((int) x); // int -> void
+ mh_vj.invokeExact((long) x); // long -> void
+ mh_vf.invokeExact((float) x); // float -> void
+ mh_vd.invokeExact((double) x); // double -> void
+ }
+
+ private static void prim2void_invalid(int x) throws Throwable {
+ // no cases
+ }
+
+ private static boolean identity(boolean v) { return v; }
+ private static byte identity(byte v) { return v; }
+ private static char identity(char v) { return v; }
+ private static short identity(short v) { return v; }
+ private static int identity(int v) { return v; }
+ private static long identity(long v) { return v; }
+ private static float identity(float v) { return v; }
+ private static double identity(double v) { return v; }
+ private static void identity() {}
+}
--- a/jdk/test/java/lang/invoke/InvokeGenericTest.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/test/java/lang/invoke/InvokeGenericTest.java Tue May 24 14:32:10 2011 -0700
@@ -24,7 +24,7 @@
*/
/* @test
- * @summary unit tests for java.lang.invoke.MethodHandle.invokeGeneric
+ * @summary unit tests for java.lang.invoke.MethodHandle.invoke
* @compile -target 7 InvokeGenericTest.java
* @run junit/othervm test.java.lang.invoke.InvokeGenericTest
*/
@@ -53,6 +53,10 @@
if (vstr != null) verbosity = Integer.parseInt(vstr);
}
+ public static void main(String... av) throws Throwable {
+ new InvokeGenericTest().testFirst();
+ }
+
@Test
public void testFirst() throws Throwable {
verbosity += 9; try {
@@ -103,7 +107,7 @@
void startTest(String name) {
if (testName != null) printCounts();
if (verbosity >= 1)
- System.out.println(name);
+ System.out.println("["+name+"]");
posTests = negTests = 0;
testName = name;
}
@@ -350,6 +354,30 @@
String[] args = { "one", "two" };
MethodHandle mh = callable(Object.class, String.class);
Object res; List resl;
+ res = resl = (List) mh.invoke((String)args[0], (Object)args[1]);
+ //System.out.println(res);
+ assertEquals(Arrays.asList(args), res);
+ }
+
+ @Test
+ public void testSimplePrims() throws Throwable {
+ startTest("testSimplePrims");
+ countTest();
+ int[] args = { 1, 2 };
+ MethodHandle mh = callable(Object.class, Object.class);
+ Object res; List resl;
+ res = resl = (List) mh.invoke(args[0], args[1]);
+ //System.out.println(res);
+ assertEquals(Arrays.toString(args), res.toString());
+ }
+
+ @Test
+ public void testAlternateName() throws Throwable {
+ startTest("testAlternateName");
+ countTest();
+ String[] args = { "one", "two" };
+ MethodHandle mh = callable(Object.class, String.class);
+ Object res; List resl;
res = resl = (List) mh.invokeGeneric((String)args[0], (Object)args[1]);
//System.out.println(res);
assertEquals(Arrays.asList(args), res);
@@ -388,24 +416,24 @@
try {
switch (args.length) {
case 0:
- junk = target.invokeGeneric(); break;
+ junk = target.invoke(); break;
case 1:
- junk = target.invokeGeneric(args[0]); break;
+ junk = target.invoke(args[0]); break;
case 2:
- junk = target.invokeGeneric(args[0], args[1]); break;
+ junk = target.invoke(args[0], args[1]); break;
case 3:
- junk = target.invokeGeneric(args[0], args[1], args[2]); break;
+ junk = target.invoke(args[0], args[1], args[2]); break;
case 4:
- junk = target.invokeGeneric(args[0], args[1], args[2], args[3]); break;
+ junk = target.invoke(args[0], args[1], args[2], args[3]); break;
default:
junk = target.invokeWithArguments(args); break;
}
} catch (WrongMethodTypeException ex) {
return;
} catch (Exception ex) {
- throw new RuntimeException("wrong exception calling "+target+target.type()+" on "+Arrays.asList(args)+" : "+ex);
+ throw new RuntimeException("wrong exception calling "+target+" on "+Arrays.asList(args), ex);
}
- throw new RuntimeException("bad success calling "+target+target.type()+" on "+Arrays.asList(args));
+ throw new RuntimeException("bad success calling "+target+" on "+Arrays.asList(args));
}
/** Make a list of all combinations of the given types, with the given arities.
@@ -451,7 +479,7 @@
startTest("testReferenceConversions");
toString_MH = LOOKUP.
findVirtual(Object.class, "toString", MethodType.methodType(String.class));
- String[] args = { "one", "two" };
+ Object[] args = { "one", "two" };
for (MethodType type : allMethodTypes(2, Object.class, String.class, RandomInterface.class)) {
testReferenceConversions(type, args);
}
@@ -463,7 +491,7 @@
MethodHandle tsdrop = MethodHandles.dropArguments(toString_MH, 1, type.parameterList());
mh = MethodHandles.foldArguments(tsdrop, mh);
mh = mh.asType(type);
- Object res = mh.invokeGeneric((String)args[0], (Object)args[1]);
+ Object res = mh.invoke((String)args[0], (Object)args[1]);
//System.out.println(res);
assertEquals(Arrays.asList(args).toString(), res);
}
@@ -473,10 +501,10 @@
public void testBoxConversions() throws Throwable {
startTest("testBoxConversions");
countTest();
- Integer[] args = { 1, 2 };
+ Object[] args = { 1, 2 };
MethodHandle mh = callable(Object.class, int.class);
Object res; List resl;
- res = resl = (List) mh.invokeGeneric((int)args[0], (Object)args[1]);
+ res = resl = (List) mh.invoke((int)args[0], (Object)args[1]);
//System.out.println(res);
assertEquals(Arrays.asList(args), res);
}
--- a/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/test/java/lang/invoke/JavaDocExamplesTest.java Tue May 24 14:32:10 2011 -0700
@@ -170,8 +170,8 @@
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
-x = mh.invokeGeneric("one", "two");
-// invokeGeneric(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+x = mh.invoke("one", "two");
+// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assert(x.equals(java.util.Arrays.asList("one","two")));
// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
@@ -199,12 +199,12 @@
MethodHandle asList = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
.asVarargsCollector(Object[].class);
-assertEquals("[]", asList.invokeGeneric().toString());
-assertEquals("[1]", asList.invokeGeneric(1).toString());
-assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
+assertEquals("[]", asList.invoke().toString());
+assertEquals("[1]", asList.invoke(1).toString());
+assertEquals("[two, too]", asList.invoke("two", "too").toString());
Object[] argv = { "three", "thee", "tee" };
-assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
-List ls = (List) asList.invokeGeneric((Object)argv);
+assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
+List ls = (List) asList.invoke((Object)argv);
assertEquals(1, ls.size());
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
}}
@@ -218,9 +218,9 @@
.asVarargsCollector(Object[].class);
MethodHandle mh = MethodHandles.exactInvoker(vamh.type()).bindTo(vamh);
assert(vamh.type().equals(mh.type()));
-assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
+assertEquals("[1, 2, 3]", vamh.invoke(1,2,3).toString());
boolean failed = false;
-try { mh.invokeGeneric(1,2,3); }
+try { mh.invoke(1,2,3); }
catch (WrongMethodTypeException ex) { failed = true; }
assert(failed);
{}
--- a/jdk/test/java/lang/invoke/MethodHandlesTest.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java Tue May 24 14:32:10 2011 -0700
@@ -105,24 +105,6 @@
public MethodHandlesTest() {
}
- @Before
- public void checkImplementedPlatform() {
- boolean platformOK = false;
- Properties properties = System.getProperties();
- String vers = properties.getProperty("java.vm.version");
- String name = properties.getProperty("java.vm.name");
- String arch = properties.getProperty("os.arch");
- if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") ||
- arch.equals("sparc") || arch.equals("sparcv9")) &&
- (name.contains("Client") || name.contains("Server"))
- ) {
- platformOK = true;
- } else {
- System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch));
- }
- assumeTrue(platformOK);
- }
-
String testName;
static int allPosTests, allNegTests;
int posTests, negTests;
@@ -331,6 +313,9 @@
static MethodHandle varargsArray(int arity) {
return ValueConversions.varargsArray(arity);
}
+ static MethodHandle varargsArray(Class<?> arrayType, int arity) {
+ return ValueConversions.varargsArray(arrayType, arity);
+ }
/** Variation of varargsList, but with the given rtype. */
static MethodHandle varargsList(int arity, Class<?> rtype) {
MethodHandle list = varargsList(arity);
@@ -865,7 +850,7 @@
Class<?> type = (Class<?>) t[1];
Object value;
Field field;
- try {
+ try {
field = HasFields.class.getDeclaredField(name);
} catch (Exception ex) {
throw new InternalError("no field HasFields."+name);
@@ -1144,16 +1129,9 @@
: MethodHandles.arrayElementSetter(arrayType);
assertSame(mh.type(), expType);
if (elemType != int.class && elemType != boolean.class) {
- MethodType gtype;
- if (true) { // FIXME: remove this path (and remove <void> below in the mh.invokes)
- gtype = mh.type().changeParameterType(0, Object.class);
- if (testSetter)
- gtype = gtype.changeParameterType(2, Object.class);
- else
- gtype = gtype.changeReturnType(Object.class);
- } else
- // FIXME: This simpler path hits a bug in convertArguments => ToGeneric
- gtype = mh.type().generic().changeParameterType(1, int.class);
+ // FIXME: change Integer.class and (Integer) below to int.class and (int) below.
+ MethodType gtype = mh.type().generic().changeParameterType(1, Integer.class);
+ if (testSetter) gtype = gtype.changeReturnType(void.class);
mh = MethodHandles.convertArguments(mh, gtype);
}
Object sawValue, expValue;
@@ -1169,7 +1147,7 @@
else if (elemType == boolean.class)
mh.invokeExact((boolean[]) array, i, (boolean)random);
else
- mh.invokeExact(array, i, random);
+ mh.invokeExact(array, (Integer)i, random);
assertEquals(model, array2list(array));
} else {
Array.set(array, i, random);
@@ -1189,7 +1167,7 @@
else if (elemType == boolean.class)
sawValue = (boolean) mh.invokeExact((boolean[]) array, i);
else
- sawValue = mh.invokeExact(array, i);
+ sawValue = mh.invokeExact(array, (Integer)i);
assertEquals(sawValue, expValue);
assertEquals(model, array2list(array));
}
@@ -1341,21 +1319,15 @@
int numcases = 1;
for (int outargs = 0; outargs <= max; outargs++) {
if (outargs - inargs >= MAX_ARG_INCREASE) continue;
- int[] reorder = new int[outargs];
int casStep = dilution + 1;
// Avoid some common factors:
while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) ||
(casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0))
casStep++;
- for (int cas = 0; cas < numcases; cas += casStep) {
- for (int i = 0, c = cas; i < outargs; i++) {
- reorder[i] = c % inargs;
- c /= inargs;
- }
- testPermuteArguments(args, types, reorder);
- }
+ testPermuteArguments(args, types, outargs, numcases, casStep);
numcases *= inargs;
if (dilution > 10 && outargs >= 4) {
+ int[] reorder = new int[outargs];
// Do some special patterns, which we probably missed.
// Replication of a single argument or argument pair.
for (int i = 0; i < inargs; i++) {
@@ -1383,6 +1355,19 @@
}
}
+ public void testPermuteArguments(Object[] args, Class<?>[] types,
+ int outargs, int numcases, int casStep) throws Throwable {
+ int inargs = args.length;
+ int[] reorder = new int[outargs];
+ for (int cas = 0; cas < numcases; cas += casStep) {
+ for (int i = 0, c = cas; i < outargs; i++) {
+ reorder[i] = c % inargs;
+ c /= inargs;
+ }
+ testPermuteArguments(args, types, reorder);
+ }
+ }
+
static int[] reverse(int[] reorder) {
reorder = reorder.clone();
for (int i = 0, imax = reorder.length / 2; i < imax; i++) {
@@ -1433,6 +1418,12 @@
MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder);
Object result = newTarget.invokeWithArguments(args);
Object expected = Arrays.asList(permArgs);
+ if (!expected.equals(result)) {
+ System.out.println("*** failed permuteArguments "+Arrays.toString(reorder)+" types="+Arrays.asList(types));
+ System.out.println("in args: "+Arrays.asList(args));
+ System.out.println("out args: "+expected);
+ System.out.println("bad args: "+result);
+ }
assertEquals(expected, result);
}
@@ -1456,26 +1447,27 @@
}
public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
countTest();
- MethodHandle target = varargsArray(nargs);
- MethodHandle target2 = changeArgTypes(target, argType);
+ Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
+ MethodHandle target2 = varargsArray(arrayType, nargs);
+ MethodHandle target = target2.asType(target2.type().generic());
if (verbosity >= 3)
System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
Object[] args = randomArgs(target2.type().parameterArray());
// make sure the target does what we think it does:
- if (pos == 0 && nargs < 5) {
- Object[] check = (Object[]) target.invokeWithArguments(args);
+ if (pos == 0 && nargs < 5 && !argType.isPrimitive()) {
+ Object[] check = (Object[]) (Object) target.invokeWithArguments(args);
assertArrayEquals(args, check);
switch (nargs) {
case 0:
- check = (Object[]) target.invokeExact();
+ check = (Object[]) (Object) target.invokeExact();
assertArrayEquals(args, check);
break;
case 1:
- check = (Object[]) target.invokeExact(args[0]);
+ check = (Object[]) (Object) target.invokeExact(args[0]);
assertArrayEquals(args, check);
break;
case 2:
- check = (Object[]) target.invokeExact(args[0], args[1]);
+ check = (Object[]) (Object) target.invokeExact(args[0], args[1]);
assertArrayEquals(args, check);
break;
}
@@ -1483,23 +1475,50 @@
List<Class<?>> newParams = new ArrayList<Class<?>>(target2.type().parameterList());
{ // modify newParams in place
List<Class<?>> spreadParams = newParams.subList(pos, nargs);
- spreadParams.clear(); spreadParams.add(Object[].class);
+ spreadParams.clear(); spreadParams.add(arrayType);
}
- MethodType newType = MethodType.methodType(Object.class, newParams);
- MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType);
- Object[] returnValue;
+ MethodType newType = MethodType.methodType(arrayType, newParams);
+ MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
+ assert(result.type() == newType) : Arrays.asList(result, newType);
+ result = result.asType(newType.generic());
+ Object returnValue;
if (pos == 0) {
- // In the following line, the first cast implies
- // normal Object return value for the MH call (Object[])->Object,
- // while the second cast dynamically converts to an Object array.
- // Such a double cast is typical of MH.invokeExact.
- returnValue = (Object[]) (Object) result.invokeExact(args);
+ Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
+ returnValue = result.invokeExact(args2);
} else {
Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
- args1[pos] = Arrays.copyOfRange(args, pos, args.length);
- returnValue = (Object[]) result.invokeWithArguments(args1);
+ args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
+ returnValue = result.invokeWithArguments(args1);
}
- assertArrayEquals(args, returnValue);
+ String argstr = Arrays.toString(args);
+ if (!argType.isPrimitive()) {
+ Object[] rv = (Object[]) returnValue;
+ String rvs = Arrays.toString(rv);
+ if (!Arrays.equals(args, rv)) {
+ System.out.println("method: "+result);
+ System.out.println("expected: "+argstr);
+ System.out.println("returned: "+rvs);
+ assertArrayEquals(args, rv);
+ }
+ } else if (argType == int.class) {
+ String rvs = Arrays.toString((int[]) returnValue);
+ if (!argstr.equals(rvs)) {
+ System.out.println("method: "+result);
+ System.out.println("expected: "+argstr);
+ System.out.println("returned: "+rvs);
+ assertEquals(argstr, rvs);
+ }
+ } else if (argType == long.class) {
+ String rvs = Arrays.toString((long[]) returnValue);
+ if (!argstr.equals(rvs)) {
+ System.out.println("method: "+result);
+ System.out.println("expected: "+argstr);
+ System.out.println("returned: "+rvs);
+ assertEquals(argstr, rvs);
+ }
+ } else {
+ // cannot test...
+ }
}
@Test
@@ -1780,7 +1799,7 @@
assertCalled("invokee", args);
// generic invoker
countTest();
- inv = MethodHandles.genericInvoker(type);
+ inv = MethodHandles.invoker(type);
if (nargs <= 3) {
calledLog.clear();
switch (nargs) {
@@ -2130,15 +2149,12 @@
Object z = surprise.invokeExact(x);
System.out.println("Failed to throw; got z="+z);
assertTrue(false);
- } catch (Exception ex) {
+ } catch (ClassCastException ex) {
if (verbosity > 2)
System.out.println("caught "+ex);
if (verbosity > 3)
ex.printStackTrace();
- assertTrue(ex instanceof ClassCastException
- // FIXME: accept only one of the two for any given unit test
- || ex instanceof WrongMethodTypeException
- );
+ assertTrue(true); // all is well
}
}
@@ -2328,6 +2344,34 @@
// else need to spin bytecode or do something else fancy
throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs);
}
+ public static MethodHandle varargsArray(Class<?> arrayType, int nargs) {
+ Class<?> elemType = arrayType.getComponentType();
+ MethodType vaType = MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType));
+ MethodHandle mh = varargsArray(nargs);
+ if (arrayType != Object[].class)
+ mh = MethodHandles.filterReturnValue(mh, CHANGE_ARRAY_TYPE.bindTo(arrayType));
+ return mh.asType(vaType);
+ }
+ static Object changeArrayType(Class<?> arrayType, Object[] a) {
+ Class<?> elemType = arrayType.getComponentType();
+ if (!elemType.isPrimitive())
+ return Arrays.copyOf(a, a.length, arrayType.asSubclass(Object[].class));
+ Object b = java.lang.reflect.Array.newInstance(elemType, a.length);
+ for (int i = 0; i < a.length; i++)
+ java.lang.reflect.Array.set(b, i, a[i]);
+ return b;
+ }
+ private static final MethodHandle CHANGE_ARRAY_TYPE;
+ static {
+ try {
+ CHANGE_ARRAY_TYPE = IMPL_LOOKUP.findStatic(ValueConversions.class, "changeArrayType",
+ MethodType.methodType(Object.class, Class.class, Object[].class));
+ } catch (NoSuchMethodException | IllegalAccessException ex) {
+ Error err = new InternalError("uncaught exception");
+ err.initCause(ex);
+ throw err;
+ }
+ }
private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY);
private static List<Object> makeList(Object... args) { return Arrays.asList(args); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/RicochetTest.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary unit tests for recursive method handles
+ * @run junit/othervm -DRicochetTest.MAX_ARITY=255 test.java.lang.invoke.RicochetTest
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.invoke.*;
+import java.util.*;
+import org.junit.*;
+import static java.lang.invoke.MethodType.*;
+import static java.lang.invoke.MethodHandles.*;
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+
+/**
+ *
+ * @author jrose
+ */
+public class RicochetTest {
+ private static final Class CLASS = RicochetTest.class;
+ private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
+
+ public static void main(String... av) throws Throwable {
+ RicochetTest test = new RicochetTest();
+ if (av.length > 0) test.testOnly = Arrays.asList(av).toString();
+ if (REPEAT == 1 || test.testOnly != null) {
+ test.testAll();
+ if (test.testOnlyTests == null) throw new RuntimeException("no matching test: "+test.testOnly);
+ } else if (REPEAT == 0) {
+ org.junit.runner.JUnitCore.runClasses(RicochetTest.class);
+ } else {
+ verbose(1, "REPEAT="+REPEAT);
+ for (int i = 0; i < REPEAT; i++) {
+ test.testRepetition = (i+1);
+ verbose(0, "[#"+test.testRepetition+"]");
+ test.testAll();
+ }
+ }
+ }
+ int testRepetition;
+
+ public void testAll() throws Throwable {
+ testNull();
+ testBoxInteger();
+ testFilterReturnValue();
+ testFilterObject();
+ testBoxLong();
+ testFilterInteger();
+ testIntSpreads();
+ testByteSpreads();
+ testLongSpreads();
+ testIntCollects();
+ testReturns();
+ }
+
+ @Test
+ public void testNull() throws Throwable {
+ if (testRepetition > (1+REPEAT/100)) return; // trivial test
+ if (!startTest("testNull")) return;
+ assertEquals(opI(37), opI.invokeWithArguments(37));
+ assertEqualFunction(opI, opI);
+ }
+
+ @Test
+ public void testBoxInteger() throws Throwable {
+ if (!startTest("testBoxInteger")) return;
+ assertEqualFunction(opI, opI.asType(opL_I.type()).asType(opI.type()));
+ }
+
+ @Test
+ public void testFilterReturnValue() throws Throwable {
+ if (!startTest("testFilterReturnValue")) return;
+ int[] ints = { 12, 23, 34, 45, 56, 67, 78, 89 };
+ Object res = list8ints.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
+ assertEquals(Arrays.toString(ints), res.toString());
+ MethodHandle idreturn = filterReturnValue(list8ints, identity(Object.class));
+ res = idreturn.invokeExact(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7]);
+ assertEquals(Arrays.toString(ints), res.toString());
+ MethodHandle add0 = addL.bindTo(0);
+ assertEqualFunction(filterReturnValue(opL2, add0), opL2);
+ }
+
+ @Test
+ public void testFilterObject() throws Throwable {
+ if (!startTest("testFilterObject")) return;
+ MethodHandle add0 = addL.bindTo(0);
+ assertEqualFunction(sequence(opL2, add0), opL2);
+ int bump13 = -13; // value near 20 works as long as test values are near [-80..80]
+ MethodHandle add13 = addL.bindTo(bump13);
+ MethodHandle add13_0 = addL.bindTo(opI2(bump13, 0));
+ MethodHandle add13_1 = addL.bindTo(opI2(0, bump13));
+ assertEqualFunction(sequence(opL2, add13_0),
+ filterArguments(opL2, 0, add13));
+ assertEqualFunction(sequence(opL2, add13_1),
+ filterArguments(opL2, 1, add13));
+ System.out.println("[testFilterObject done]");
+ }
+
+ @Test
+ public void testBoxLong() throws Throwable {
+ if (!startTest("testBoxLong")) return;
+ assertEqualFunction(opJ, opJ.asType(opL_J.type()).asType(opJ.type()));
+ }
+
+ @Test
+ public void testFilterInteger() throws Throwable {
+ if (!startTest("testFilterInteger")) return;
+ assertEqualFunction(opI, sequence(convI_L, opL_I));
+ }
+
+ @Test
+ public void testIntSpreads() throws Throwable {
+ if (!startTest("testIntSpreads")) return;
+ MethodHandle id = identity(int[].class);
+ final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
+ for (int nargs = 0; nargs <= MAX; nargs++) {
+ if (nargs > 30 && nargs < MAX-20) nargs += 10;
+ int[] args = new int[nargs];
+ for (int j = 0; j < args.length; j++) args[j] = (int)(j + 11);
+ //System.out.println("testIntSpreads "+Arrays.toString(args));
+ int[] args1 = (int[]) id.invokeExact(args);
+ assertArrayEquals(args, args1);
+ MethodHandle coll = id.asCollector(int[].class, nargs);
+ int[] args2 = args;
+ switch (nargs) {
+ case 0: args2 = (int[]) coll.invokeExact(); break;
+ case 1: args2 = (int[]) coll.invokeExact(args[0]); break;
+ case 2: args2 = (int[]) coll.invokeExact(args[0], args[1]); break;
+ case 3: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2]); break;
+ case 4: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
+ case 5: args2 = (int[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
+ }
+ assertArrayEquals(args, args2);
+ MethodHandle mh = coll.asSpreader(int[].class, nargs);
+ int[] args3 = (int[]) mh.invokeExact(args);
+ assertArrayEquals(args, args3);
+ }
+ }
+
+ @Test
+ public void testByteSpreads() throws Throwable {
+ if (!startTest("testByteSpreads")) return;
+ MethodHandle id = identity(byte[].class);
+ final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
+ for (int nargs = 0; nargs <= MAX; nargs++) {
+ if (nargs > 30 && nargs < MAX-20) nargs += 10;
+ byte[] args = new byte[nargs];
+ for (int j = 0; j < args.length; j++) args[j] = (byte)(j + 11);
+ //System.out.println("testByteSpreads "+Arrays.toString(args));
+ byte[] args1 = (byte[]) id.invokeExact(args);
+ assertArrayEquals(args, args1);
+ MethodHandle coll = id.asCollector(byte[].class, nargs);
+ byte[] args2 = args;
+ switch (nargs) {
+ case 0: args2 = (byte[]) coll.invokeExact(); break;
+ case 1: args2 = (byte[]) coll.invokeExact(args[0]); break;
+ case 2: args2 = (byte[]) coll.invokeExact(args[0], args[1]); break;
+ case 3: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2]); break;
+ case 4: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
+ case 5: args2 = (byte[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
+ }
+ assertArrayEquals(args, args2);
+ MethodHandle mh = coll.asSpreader(byte[].class, nargs);
+ byte[] args3 = (byte[]) mh.invokeExact(args);
+ assertArrayEquals(args, args3);
+ }
+ }
+
+ @Test
+ public void testLongSpreads() throws Throwable {
+ if (!startTest("testLongSpreads")) return;
+ MethodHandle id = identity(long[].class);
+ final int MAX = (MAX_ARITY - 2) / 2; // 253/2+1 would cause parameter overflow with 'this' added
+ for (int nargs = 0; nargs <= MAX; nargs++) {
+ if (nargs > 30 && nargs < MAX-20) nargs += 10;
+ long[] args = new long[nargs];
+ for (int j = 0; j < args.length; j++) args[j] = (long)(j + 11);
+ //System.out.println("testLongSpreads "+Arrays.toString(args));
+ long[] args1 = (long[]) id.invokeExact(args);
+ assertArrayEquals(args, args1);
+ MethodHandle coll = id.asCollector(long[].class, nargs);
+ long[] args2 = args;
+ switch (nargs) {
+ case 0: args2 = (long[]) coll.invokeExact(); break;
+ case 1: args2 = (long[]) coll.invokeExact(args[0]); break;
+ case 2: args2 = (long[]) coll.invokeExact(args[0], args[1]); break;
+ case 3: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2]); break;
+ case 4: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3]); break;
+ case 5: args2 = (long[]) coll.invokeExact(args[0], args[1], args[2], args[3], args[4]); break;
+ }
+ assertArrayEquals(args, args2);
+ MethodHandle mh = coll.asSpreader(long[].class, nargs);
+ long[] args3 = (long[]) mh.invokeExact(args);
+ assertArrayEquals(args, args3);
+ }
+ }
+
+ @Test
+ public void testIntCollects() throws Throwable {
+ if (!startTest("testIntCollects")) return;
+ for (MethodHandle lister : INT_LISTERS) {
+ int outputs = lister.type().parameterCount();
+ for (int collects = 0; collects <= Math.min(outputs, INT_COLLECTORS.length-1); collects++) {
+ int inputs = outputs - 1 + collects;
+ if (inputs < 0) continue;
+ for (int pos = 0; pos + collects <= inputs; pos++) {
+ MethodHandle collector = INT_COLLECTORS[collects];
+ int[] args = new int[inputs];
+ int ap = 0, arg = 31;
+ for (int i = 0; i < pos; i++)
+ args[ap++] = arg++ + 0;
+ for (int i = 0; i < collects; i++)
+ args[ap++] = arg++ + 10;
+ while (ap < args.length)
+ args[ap++] = arg++ + 20;
+ // calculate piecemeal:
+ //System.out.println("testIntCollects "+Arrays.asList(lister, pos, collector)+" on "+Arrays.toString(args));
+ int[] collargs = Arrays.copyOfRange(args, pos, pos+collects);
+ int coll = (int) collector.asSpreader(int[].class, collargs.length).invokeExact(collargs);
+ int[] listargs = Arrays.copyOfRange(args, 0, outputs);
+ System.arraycopy(args, pos+collects, listargs, pos+1, outputs - (pos+1));
+ listargs[pos] = coll;
+ //System.out.println(" coll="+coll+" listargs="+Arrays.toString(listargs));
+ Object expect = lister.asSpreader(int[].class, listargs.length).invokeExact(listargs);
+ //System.out.println(" expect="+expect);
+
+ // now use the combined MH, and test the output:
+ MethodHandle mh = collectArguments(lister, pos, INT_COLLECTORS[collects]);
+ if (mh == null) continue; // no infix collection, yet
+ assert(mh.type().parameterCount() == inputs);
+ Object observe = mh.asSpreader(int[].class, args.length).invokeExact(args);
+ assertEquals(expect, observe);
+ }
+ }
+ }
+ }
+
+ private static MethodHandle collectArguments(MethodHandle lister, int pos, MethodHandle collector) {
+ int collects = collector.type().parameterCount();
+ int outputs = lister.type().parameterCount();
+ if (pos == outputs - 1)
+ return MethodHandles.filterArguments(lister, pos,
+ collector.asSpreader(int[].class, collects))
+ .asCollector(int[].class, collects);
+ //return MethodHandles.collectArguments(lister, pos, collector); //no such animal
+ return null;
+ }
+
+ private static final Class<?>[] RETURN_TYPES = {
+ Object.class, String.class, Integer.class,
+ int.class, long.class,
+ boolean.class, byte.class, char.class, short.class,
+ float.class, double.class,
+ void.class,
+ };
+
+ @Test
+ public void testReturns() throws Throwable {
+ if (!startTest("testReturns")) return;
+ // fault injection:
+ int faultCount = 0; // total of 1296 tests
+ faultCount = Integer.getInteger("testReturns.faultCount", 0);
+ for (Class<?> ret : RETURN_TYPES) {
+ // make a complicated identity function and pass something through it
+ System.out.println(ret.getSimpleName());
+ Class<?> vret = (ret == void.class) ? Void.class : ret;
+ MethodHandle id = // (vret)->ret
+ identity(vret).asType(methodType(ret, vret));
+ final int LENGTH = 4;
+ int[] index = {0};
+ Object vals = java.lang.reflect.Array.newInstance(vret, LENGTH);
+ MethodHandle indexGetter = //()->int
+ insertArguments(arrayElementGetter(index.getClass()), 0, index, 0);
+ MethodHandle valSelector = // (int)->vret
+ arrayElementGetter(vals.getClass()).bindTo(vals);
+ MethodHandle valGetter = // ()->vret
+ foldArguments(valSelector, indexGetter);
+ if (ret != void.class) {
+ for (int i = 0; i < LENGTH; i++) {
+ Object val = (i + 50);
+ if (ret == boolean.class) val = (i % 3 == 0);
+ if (ret == String.class) val = "#"+i;
+ if (ret == char.class) val = (char)('a'+i);
+ if (ret == byte.class) val = (byte)~i;
+ if (ret == short.class) val = (short)(1<<i);
+ java.lang.reflect.Array.set(vals, i, val);
+ }
+ }
+ for (int i = 0; i < LENGTH; i++) {
+ Object val = java.lang.reflect.Array.get(vals, i);
+ System.out.println(i+" => "+val);
+ index[0] = i;
+ if (--faultCount == 0) index[0] ^= 1;
+ Object x = valGetter.invokeWithArguments();
+ assertEquals(val, x);
+ // make a return-filter call: x = id(valGetter())
+ if (--faultCount == 0) index[0] ^= 1;
+ x = filterReturnValue(valGetter, id).invokeWithArguments();
+ assertEquals(val, x);
+ // make a filter call: x = id(*,valGetter(),*)
+ for (int len = 1; len <= 4; len++) {
+ for (int pos = 0; pos < len; pos++) {
+ MethodHandle proj = id; // lambda(..., vret x,...){x}
+ for (int j = 0; j < len; j++) {
+ if (j == pos) continue;
+ proj = dropArguments(proj, j, Object.class);
+ }
+ assert(proj.type().parameterCount() == len);
+ // proj: (Object*, pos: vret, Object*)->ret
+ assertEquals(vret, proj.type().parameterType(pos));
+ MethodHandle vgFilter = dropArguments(valGetter, 0, Object.class);
+ if (--faultCount == 0) index[0] ^= 1;
+ x = filterArguments(proj, pos, vgFilter).invokeWithArguments(new Object[len]);
+ assertEquals(val, x);
+ }
+ }
+ // make a fold call:
+ for (int len = 0; len <= 4; len++) {
+ for (int fold = 0; fold <= len; fold++) {
+ MethodHandle proj = id; // lambda(ret x, ...){x}
+ if (ret == void.class) proj = constant(Object.class, null);
+ int arg0 = (ret == void.class ? 0 : 1);
+ for (int j = 0; j < len; j++) {
+ proj = dropArguments(proj, arg0, Object.class);
+ }
+ assert(proj.type().parameterCount() == arg0 + len);
+ // proj: (Object*, pos: vret, Object*)->ret
+ if (arg0 != 0) assertEquals(vret, proj.type().parameterType(0));
+ MethodHandle vgFilter = valGetter.asType(methodType(ret));
+ for (int j = 0; j < fold; j++) {
+ vgFilter = dropArguments(vgFilter, j, Object.class);
+ }
+ x = foldArguments(proj, vgFilter).invokeWithArguments(new Object[len]);
+ if (--faultCount == 0) index[0] ^= 1;
+ assertEquals(val, x);
+ }
+ }
+ }
+ }
+ //System.out.println("faultCount="+faultCount);
+ }
+
+ private static MethodHandle sequence(MethodHandle mh1, MethodHandle... mhs) {
+ MethodHandle res = mh1;
+ for (MethodHandle mh2 : mhs)
+ res = filterReturnValue(res, mh2);
+ return res;
+ }
+ private static void assertEqualFunction(MethodHandle x, MethodHandle y) throws Throwable {
+ assertEquals(x.type(), y.type()); //??
+ MethodType t = x.type();
+ if (t.parameterCount() == 0) {
+ assertEqualFunctionAt(null, x, y);
+ return;
+ }
+ Class<?> ptype = t.parameterType(0);
+ if (ptype == long.class || ptype == Long.class) {
+ for (long i = -10; i <= 10; i++) {
+ assertEqualFunctionAt(i, x, y);
+ }
+ } else {
+ for (int i = -10; i <= 10; i++) {
+ assertEqualFunctionAt(i, x, y);
+ }
+ }
+ }
+ private static void assertEqualFunctionAt(Object v, MethodHandle x, MethodHandle y) throws Throwable {
+ Object[] args = new Object[x.type().parameterCount()];
+ Arrays.fill(args, v);
+ Object xval = invokeWithCatch(x, args);
+ Object yval = invokeWithCatch(y, args);
+ String msg = "ok";
+ if (!Objects.equals(xval, yval)) {
+ msg = ("applying "+x+" & "+y+" to "+v);
+ }
+ assertEquals(msg, xval, yval);
+ }
+ private static Object invokeWithCatch(MethodHandle mh, Object... args) throws Throwable {
+ try {
+ return mh.invokeWithArguments(args);
+ } catch (Throwable ex) {
+ System.out.println("threw: "+mh+Arrays.asList(args));
+ ex.printStackTrace();
+ return ex;
+ }
+ }
+
+ private static final Lookup LOOKUP = lookup();
+ private static MethodHandle findStatic(String name,
+ Class<?> rtype,
+ Class<?>... ptypes) {
+ try {
+ return LOOKUP.findStatic(LOOKUP.lookupClass(), name, methodType(rtype, ptypes));
+ } catch (ReflectiveOperationException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ private static MethodHandle findStatic(String name,
+ Class<?> rtype,
+ List<?> ptypes) {
+ return findStatic(name, rtype, ptypes.toArray(new Class<?>[ptypes.size()]));
+ }
+ static int getProperty(String name, int dflt) {
+ String qual = LOOKUP.lookupClass().getName();
+ String prop = System.getProperty(qual+"."+name);
+ if (prop == null) prop = System.getProperty(name);
+ if (prop == null) return dflt;
+ return Integer.parseInt(prop);
+ }
+
+ private static int opI(int... xs) {
+ stress();
+ int base = 100;
+ int z = 0;
+ for (int x : xs) {
+ z = (z * base) + (x % base);
+ }
+ verbose("opI", xs.length, xs, z);
+ return z;
+ }
+ private static int opI2(int x, int y) { return opI(x, y); } // x*100 + y%100
+ private static int opI3(int x, int y, int z) { return opI(x, y, z); }
+ private static int opI4(int w, int x, int y, int z) { return opI(w, x, y, z); }
+ private static int opI(int x) { return opI2(x, 37); }
+ private static Object opI_L(int x) { return (Object) opI(x); }
+ private static long opJ3(long x, long y, long z) { return (long) opI3((int)x, (int)y, (int)z); }
+ private static long opJ2(long x, long y) { return (long) opI2((int)x, (int)y); }
+ private static long opJ(long x) { return (long) opI((int)x); }
+ private static Object opL2(Object x, Object y) { return (Object) opI2((int)x, (int)y); }
+ private static Object opL(Object x) { return (Object) opI((int)x); }
+ private static int opL2_I(Object x, Object y) { return (int) opI2((int)x, (int)y); }
+ private static int opL_I(Object x) { return (int) opI((int)x); }
+ private static long opL_J(Object x) { return (long) opI((int)x); }
+ private static final MethodHandle opI, opI2, opI3, opI4, opI_L, opJ, opJ2, opJ3, opL2, opL, opL2_I, opL_I, opL_J;
+ static {
+ opI4 = findStatic("opI4", int.class, int.class, int.class, int.class, int.class);
+ opI3 = findStatic("opI3", int.class, int.class, int.class, int.class);
+ opI2 = findStatic("opI2", int.class, int.class, int.class);
+ opI = findStatic("opI", int.class, int.class);
+ opI_L = findStatic("opI_L", Object.class, int.class);
+ opJ = findStatic("opJ", long.class, long.class);
+ opJ2 = findStatic("opJ2", long.class, long.class, long.class);
+ opJ3 = findStatic("opJ3", long.class, long.class, long.class, long.class);
+ opL2 = findStatic("opL2", Object.class, Object.class, Object.class);
+ opL = findStatic("opL", Object.class, Object.class);
+ opL2_I = findStatic("opL2_I", int.class, Object.class, Object.class);
+ opL_I = findStatic("opL_I", int.class, Object.class);
+ opL_J = findStatic("opL_J", long.class, Object.class);
+ }
+ private static final MethodHandle[] INT_COLLECTORS = {
+ constant(int.class, 42), opI, opI2, opI3, opI4
+ };
+ private static final MethodHandle[] LONG_COLLECTORS = {
+ constant(long.class, 42), opJ, opJ2, opJ3
+ };
+
+ private static int addI(int x, int y) { stress(); return x+y; }
+ private static Object addL(Object x, Object y) { return addI((int)x, (int)y); }
+ private static final MethodHandle addI, addL;
+ static {
+ addI = findStatic("addI", int.class, int.class, int.class);
+ addL = findStatic("addL", Object.class, Object.class, Object.class);
+ }
+
+ private static Object list8ints(int a, int b, int c, int d, int e, int f, int g, int h) {
+ return Arrays.asList(a, b, c, d, e, f, g, h);
+ }
+ private static Object list8longs(long a, long b, long c, long d, long e, long f, long g, long h) {
+ return Arrays.asList(a, b, c, d, e, f, g, h);
+ }
+ private static final MethodHandle list8ints = findStatic("list8ints", Object.class,
+ Collections.nCopies(8, int.class));
+ private static final MethodHandle list8longs = findStatic("list8longs", Object.class,
+ Collections.nCopies(8, long.class));
+ private static final MethodHandle[] INT_LISTERS, LONG_LISTERS;
+ static {
+ int listerCount = list8ints.type().parameterCount() + 1;
+ INT_LISTERS = new MethodHandle[listerCount];
+ LONG_LISTERS = new MethodHandle[listerCount];
+ MethodHandle lister = list8ints;
+ MethodHandle llister = list8longs;
+ for (int i = listerCount - 1; ; i--) {
+ INT_LISTERS[i] = lister;
+ LONG_LISTERS[i] = llister;
+ if (i == 0) break;
+ lister = insertArguments(lister, i-1, (int)0);
+ llister = insertArguments(llister, i-1, (long)0);
+ }
+ }
+
+ private static Object convI_L(int x) { stress(); return (Object) x; }
+ private static int convL_I(Object x) { stress(); return (int) x; }
+ private static Object convJ_L(long x) { stress(); return (Object) x; }
+ private static long convL_J(Object x) { stress(); return (long) x; }
+ private static int convJ_I(long x) { stress(); return (int) x; }
+ private static long convI_J(int x) { stress(); return (long) x; }
+ private static final MethodHandle convI_L, convL_I, convJ_L, convL_J, convJ_I, convI_J;
+ static {
+ convI_L = findStatic("convI_L", Object.class, int.class);
+ convL_I = findStatic("convL_I", int.class, Object.class);
+ convJ_L = findStatic("convJ_L", Object.class, long.class);
+ convL_J = findStatic("convL_J", long.class, Object.class);
+ convJ_I = findStatic("convJ_I", int.class, long.class);
+ convI_J = findStatic("convI_J", long.class, int.class);
+ }
+
+ // stress modes:
+ private static final int REPEAT = getProperty("REPEAT", 0);
+ private static final int STRESS = getProperty("STRESS", 0);
+ private static /*v*/ int STRESS_COUNT;
+ private static final Object[] SINK = new Object[4];
+ private static void stress() {
+ if (STRESS <= 0) return;
+ int count = STRESS + (STRESS_COUNT++ & 0x1); // non-constant value
+ for (int i = 0; i < count; i++) {
+ SINK[i % SINK.length] = new Object[STRESS + i % (SINK.length + 1)];
+ }
+ }
+
+ // verbosity:
+ private static final int VERBOSITY = getProperty("VERBOSITY", 0) + (REPEAT == 0 ? 0 : -1);
+ private static void verbose(Object a, Object b, Object c, Object d) {
+ if (VERBOSITY <= 0) return;
+ verbose(1, a, b, c, d);
+ }
+ private static void verbose(Object a, Object b, Object c) {
+ if (VERBOSITY <= 0) return;
+ verbose(1, a, b, c);
+ }
+ private static void verbose(int level, Object a, Object... bcd) {
+ if (level > VERBOSITY) return;
+ String m = a.toString();
+ if (bcd != null && bcd.length > 0) {
+ List<Object> l = new ArrayList<>(bcd.length);
+ for (Object x : bcd) {
+ if (x instanceof Object[]) x = Arrays.asList((Object[])x);
+ if (x instanceof int[]) x = Arrays.toString((int[])x);
+ if (x instanceof long[]) x = Arrays.toString((long[])x);
+ l.add(x);
+ }
+ m = m+Arrays.asList(bcd);
+ }
+ System.out.println(m);
+ }
+ String testOnly;
+ String testOnlyTests;
+ private boolean startTest(String name) {
+ if (testOnly != null && !testOnly.contains(name))
+ return false;
+ verbose(0, "["+name+"]");
+ testOnlyTests = (testOnlyTests == null) ? name : testOnlyTests+" "+name;
+ return true;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/text/Bidi/Bug7041232.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7041232
+ * @summary verify that an unexpected exception isn't thrown for unnatural data to keep backward compatibility with JDK 6.
+ */
+import java.text.*;
+
+public class Bug7041232 {
+
+ public static void main(String[] args) {
+ String UnicodeChars;
+ StringBuffer sb = new StringBuffer();
+
+ // Generates String which includes U+2028(line separator) and
+ // U+2029(paragraph separator)
+ for (int i = 0x2000; i < 0x2100; i++) {
+ sb.append((char)i);
+ }
+ UnicodeChars = sb.toString();
+
+ Bidi bidi = new Bidi(UnicodeChars, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
+ bidi.createLineBidi(0, UnicodeChars.length());
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JComboBox/7031551/bug7031551.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 7031551
+ * @summary Generics: JComboBox
+ * @author Pavel Porvatov
+ */
+
+import javax.swing.*;
+import java.util.Vector;
+
+public class bug7031551 {
+
+ private static final String TEST_ELEMENT1 = "Test1";
+ private static final String TEST_ELEMENT2 = "Test2";
+ private static final String TEST_ELEMENT3 = "Test3";
+
+ /**
+ * @param args the command line arguments
+ */
+ @SuppressWarnings("unchecked")
+ public static void main(String[] args) {
+ testRawSignatures();
+ testGenericSignatures();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void testRawSignatures() {
+ // Test JComboBox
+ ComboBoxModel rawTestModel = new DefaultComboBoxModel();
+
+ JComboBox rawTestComboBox = new JComboBox();
+ rawTestComboBox = new JComboBox(rawTestModel);
+ rawTestComboBox = new JComboBox(new Object[]{TEST_ELEMENT1});
+ rawTestComboBox = new JComboBox(new Vector());
+
+ Object unused1 = rawTestComboBox.getPrototypeDisplayValue();
+ rawTestComboBox.setPrototypeDisplayValue(TEST_ELEMENT1);
+
+ ListCellRenderer unused2 = rawTestComboBox.getRenderer();
+ rawTestComboBox.setRenderer(new DefaultListCellRenderer());
+
+ ComboBoxModel unused3 = rawTestComboBox.getModel();
+ rawTestComboBox.setModel(rawTestModel);
+
+ rawTestComboBox.addItem(TEST_ELEMENT2);
+ rawTestComboBox.insertItemAt(TEST_ELEMENT3, 1);
+ rawTestComboBox.removeItem(TEST_ELEMENT2);
+ assertEquals(rawTestComboBox.getItemAt(0), TEST_ELEMENT3);
+ rawTestComboBox.removeAllItems();
+
+ // Test DefaultComboBoxModel
+ DefaultComboBoxModel testModel = new DefaultComboBoxModel();
+ testModel = new DefaultComboBoxModel(new Vector());
+ testModel = new DefaultComboBoxModel(new Object[]{TEST_ELEMENT1});
+
+ testModel.addElement(TEST_ELEMENT2);
+ testModel.insertElementAt(TEST_ELEMENT3, 1);
+ assertEquals(testModel.getElementAt(2), TEST_ELEMENT2);
+ }
+
+ private static void testGenericSignatures() {
+ // Test JComboBox
+ ComboBoxModel<String> stringTestModel = new DefaultComboBoxModel<String>();
+
+ JComboBox<String> stringTestComboBox = new JComboBox<String>();
+ stringTestComboBox = new JComboBox<String>(stringTestModel);
+ stringTestComboBox = new JComboBox<String>(new String[]{TEST_ELEMENT1});
+ stringTestComboBox = new JComboBox<String>(new Vector<String>());
+
+ String unused1 = stringTestComboBox.getPrototypeDisplayValue();
+ stringTestComboBox.setPrototypeDisplayValue(TEST_ELEMENT1);
+
+ ListCellRenderer<? super String> unused2 = stringTestComboBox.getRenderer();
+ stringTestComboBox.setRenderer(new DefaultListCellRenderer());
+
+ ComboBoxModel unused3 = stringTestComboBox.getModel();
+ stringTestComboBox.setModel(stringTestModel);
+
+ stringTestComboBox.addItem(TEST_ELEMENT2);
+ stringTestComboBox.insertItemAt(TEST_ELEMENT3, 1);
+ stringTestComboBox.removeItem(TEST_ELEMENT2);
+ assertEquals(stringTestComboBox.getItemAt(0), TEST_ELEMENT3);
+ stringTestComboBox.removeAllItems();
+
+ // Test DefaultComboBoxModel
+ DefaultComboBoxModel<String> testModel = new DefaultComboBoxModel<String>();
+ testModel = new DefaultComboBoxModel<String>(new Vector<String>());
+ testModel = new DefaultComboBoxModel<String>(new String[]{TEST_ELEMENT1});
+
+ testModel.addElement(TEST_ELEMENT2);
+ testModel.insertElementAt(TEST_ELEMENT3, 1);
+ assertEquals(testModel.getElementAt(2), TEST_ELEMENT2);
+ }
+
+ private static void assertEquals(Object expectedObject, Object actualObject) {
+ if (!expectedObject.equals(actualObject)) {
+ throw new RuntimeException("Expected: " + expectedObject + " but was: " + actualObject);
+ }
+ }
+}
+
--- a/jdk/test/javax/swing/JTable/6788484/bug6788484.java Tue May 24 14:15:14 2011 -0700
+++ b/jdk/test/javax/swing/JTable/6788484/bug6788484.java Tue May 24 14:32:10 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011 Oracle and/or its affiliates. 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
@@ -24,9 +24,17 @@
/* @test
@bug 6788484
@summary NPE in DefaultTableCellHeaderRenderer.getColumnSortOrder() with null table
+ @compile -XDignore.symbol.file=true bug6788484.java
@author Alexander Potochkin
@run main bug6788484
*/
+
+/*
+ * Compile with -XDignore.symbol.file=true option as a workaround for
+ * specific behaviour described in 6380059 which restricts proprietary
+ * package loading
+ */
+
import sun.swing.table.DefaultTableCellHeaderRenderer;
import javax.swing.*;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/invoke/util/ValueConversionsTest.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,448 @@
+/*
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.sun.invoke.util;
+
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.Wrapper;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Ignore;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/* @test
+ * @summary unit tests for value-type conversion utilities
+ * @ignore This test requires a special compilation environment to access sun.inovke.util. Run by hand.
+ * @run junit/othervm test.sun.invoke.util.ValueConversionsTest
+ * @run junit/othervm
+ * -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.START_ARITY=250
+ * test.sun.invoke.util.ValueConversionsTest
+ */
+
+// This might take a while and burn lots of metadata:
+// @run junit/othervm -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.EXHAUSTIVE=true test.sun.invoke.util.ValueConversionsTest
+
+/**
+ *
+ * @author jrose
+ */
+public class ValueConversionsTest {
+ private static final Class CLASS = ValueConversionsTest.class;
+ private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40);
+ private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0);
+ private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE");
+
+ @Test
+ public void testUnbox() throws Throwable {
+ testUnbox(false);
+ }
+
+ @Test
+ public void testUnboxCast() throws Throwable {
+ testUnbox(true);
+ }
+
+ private void testUnbox(boolean doCast) throws Throwable {
+ //System.out.println("unbox");
+ for (Wrapper dst : Wrapper.values()) {
+ //System.out.println(dst);
+ for (Wrapper src : Wrapper.values()) {
+ testUnbox(doCast, dst, src);
+ }
+ }
+ }
+
+ private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable {
+ boolean expectThrow = !doCast && !dst.isConvertibleFrom(src);
+ if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT) return; // must have prims
+ if (dst == Wrapper.OBJECT)
+ expectThrow = false; // everything (even VOID==null here) converts to OBJECT
+ try {
+ for (int n = -5; n < 10; n++) {
+ Object box = src.wrap(n);
+ switch (src) {
+ case VOID: assertEquals(box, null); break;
+ case OBJECT: box = box.toString(); break;
+ case SHORT: assertEquals(box.getClass(), Short.class); break;
+ default: assertEquals(box.getClass(), src.wrapperType()); break;
+ }
+ MethodHandle unboxer;
+ if (doCast)
+ unboxer = ValueConversions.unboxCast(dst.primitiveType());
+ else
+ unboxer = ValueConversions.unbox(dst.primitiveType());
+ Object expResult = (box == null) ? dst.zero() : dst.wrap(box);
+ Object result = null;
+ switch (dst) {
+ case INT: result = (int) unboxer.invokeExact(box); break;
+ case LONG: result = (long) unboxer.invokeExact(box); break;
+ case FLOAT: result = (float) unboxer.invokeExact(box); break;
+ case DOUBLE: result = (double) unboxer.invokeExact(box); break;
+ case CHAR: result = (char) unboxer.invokeExact(box); break;
+ case BYTE: result = (byte) unboxer.invokeExact(box); break;
+ case SHORT: result = (short) unboxer.invokeExact(box); break;
+ case OBJECT: result = (Object) unboxer.invokeExact(box); break;
+ case BOOLEAN: result = (boolean) unboxer.invokeExact(box); break;
+ case VOID: result = null; unboxer.invokeExact(box); break;
+ }
+ if (expectThrow) {
+ expResult = "(need an exception)";
+ }
+ assertEquals("(doCast,expectThrow,dst,src,n,box)="+Arrays.asList(doCast,expectThrow,dst,src,n,box),
+ expResult, result);
+ }
+ } catch (RuntimeException ex) {
+ if (expectThrow) return;
+ System.out.println("Unexpected throw for (doCast,expectThrow,dst,src)="+Arrays.asList(doCast,expectThrow,dst,src));
+ throw ex;
+ }
+ }
+
+ @Test
+ public void testUnboxRaw() throws Throwable {
+ //System.out.println("unboxRaw");
+ for (Wrapper w : Wrapper.values()) {
+ if (w == Wrapper.OBJECT) continue; // skip this; no raw form
+ //System.out.println(w);
+ for (int n = -5; n < 10; n++) {
+ Object box = w.wrap(n);
+ long expResult = w.unwrapRaw(box);
+ Object box2 = w.wrapRaw(expResult);
+ assertEquals(box, box2);
+ MethodHandle unboxer = ValueConversions.unboxRaw(w.primitiveType());
+ long result = -1;
+ switch (w) {
+ case INT: result = (int) unboxer.invokeExact(box); break;
+ case LONG: result = (long) unboxer.invokeExact(box); break;
+ case FLOAT: result = (int) unboxer.invokeExact(box); break;
+ case DOUBLE: result = (long) unboxer.invokeExact(box); break;
+ case CHAR: result = (int) unboxer.invokeExact(box); break;
+ case BYTE: result = (int) unboxer.invokeExact(box); break;
+ case SHORT: result = (int) unboxer.invokeExact(box); break;
+ case BOOLEAN: result = (int) unboxer.invokeExact(box); break;
+ case VOID: result = (int) unboxer.invokeExact(box); break;
+ }
+ assertEquals("(w,n,box)="+Arrays.asList(w,n,box),
+ expResult, result);
+ }
+ }
+ }
+
+ @Test
+ public void testBox() throws Throwable {
+ //System.out.println("box");
+ for (Wrapper w : Wrapper.values()) {
+ if (w == Wrapper.VOID) continue; // skip this; no unboxed form
+ //System.out.println(w);
+ for (int n = -5; n < 10; n++) {
+ Object box = w.wrap(n);
+ MethodHandle boxer = ValueConversions.box(w.primitiveType());
+ Object expResult = box;
+ Object result = null;
+ switch (w) {
+ case INT: result = boxer.invokeExact((int)n); break;
+ case LONG: result = boxer.invokeExact((long)n); break;
+ case FLOAT: result = boxer.invokeExact((float)n); break;
+ case DOUBLE: result = boxer.invokeExact((double)n); break;
+ case CHAR: result = boxer.invokeExact((char)n); break;
+ case BYTE: result = boxer.invokeExact((byte)n); break;
+ case SHORT: result = boxer.invokeExact((short)n); break;
+ case OBJECT: result = boxer.invokeExact((Object)n); break;
+ case BOOLEAN: result = boxer.invokeExact((n & 1) != 0); break;
+ }
+ assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
+ expResult, result);
+ }
+ }
+ }
+
+ @Test
+ public void testBoxRaw() throws Throwable {
+ //System.out.println("boxRaw");
+ for (Wrapper w : Wrapper.values()) {
+ if (w == Wrapper.VOID) continue; // skip this; no unboxed form
+ if (w == Wrapper.OBJECT) continue; // skip this; no raw form
+ //System.out.println(w);
+ for (int n = -5; n < 10; n++) {
+ Object box = w.wrap(n);
+ long raw = w.unwrapRaw(box);
+ Object expResult = box;
+ MethodHandle boxer = ValueConversions.boxRaw(w.primitiveType());
+ Object result = null;
+ switch (w) {
+ case INT: result = boxer.invokeExact((int)raw); break;
+ case LONG: result = boxer.invokeExact(raw); break;
+ case FLOAT: result = boxer.invokeExact((int)raw); break;
+ case DOUBLE: result = boxer.invokeExact(raw); break;
+ case CHAR: result = boxer.invokeExact((int)raw); break;
+ case BYTE: result = boxer.invokeExact((int)raw); break;
+ case SHORT: result = boxer.invokeExact((int)raw); break;
+ case BOOLEAN: result = boxer.invokeExact((int)raw); break;
+ }
+ assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
+ expResult, result);
+ }
+ }
+ }
+
+ @Test
+ public void testReboxRaw() throws Throwable {
+ //System.out.println("reboxRaw");
+ for (Wrapper w : Wrapper.values()) {
+ Wrapper pw = Wrapper.forPrimitiveType(w.rawPrimitiveType());
+ if (w == Wrapper.VOID) continue; // skip this; no unboxed form
+ if (w == Wrapper.OBJECT) continue; // skip this; no raw form
+ //System.out.println(w);
+ for (int n = -5; n < 10; n++) {
+ Object box = w.wrap(n);
+ Object raw = pw.wrap(w.unwrapRaw(box));
+ Object expResult = box;
+ MethodHandle boxer = ValueConversions.rebox(w.primitiveType());
+ Object result = null;
+ switch (w) {
+ case INT: result = boxer.invokeExact(raw); break;
+ case LONG: result = boxer.invokeExact(raw); break;
+ case FLOAT: result = boxer.invokeExact(raw); break;
+ case DOUBLE: result = boxer.invokeExact(raw); break;
+ case CHAR: result = boxer.invokeExact(raw); break;
+ case BYTE: result = boxer.invokeExact(raw); break;
+ case SHORT: result = boxer.invokeExact(raw); break;
+ case BOOLEAN: result = boxer.invokeExact(raw); break;
+ }
+ assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box),
+ expResult, result);
+ }
+ }
+ }
+
+ @Test
+ public void testCast() throws Throwable {
+ //System.out.println("cast");
+ Class<?>[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class };
+ Object[] objects = { new Object(), Boolean.FALSE, "hello", (Long)12L, (Integer)6 };
+ for (Class<?> dst : types) {
+ MethodHandle caster = ValueConversions.cast(dst);
+ assertEquals(caster.type(), ValueConversions.identity().type());
+ for (Object obj : objects) {
+ Class<?> src = obj.getClass();
+ boolean canCast;
+ if (dst.isInterface()) {
+ canCast = true;
+ } else {
+ canCast = dst.isAssignableFrom(src);
+ assertEquals(canCast, dst.isInstance(obj));
+ }
+ //System.out.println("obj="+obj+" <: dst="+dst);
+ try {
+ Object result = caster.invokeExact(obj);
+ if (canCast)
+ assertEquals(obj, result);
+ else
+ assertEquals("cast should not have succeeded", dst, obj);
+ } catch (ClassCastException ex) {
+ if (canCast)
+ throw ex;
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testIdentity() throws Throwable {
+ //System.out.println("identity");
+ MethodHandle id = ValueConversions.identity();
+ Object expResult = "foo";
+ Object result = id.invokeExact(expResult);
+ // compiler bug: ValueConversions.identity().invokeExact("bar");
+ assertEquals(expResult, result);
+ }
+
+ @Test
+ public void testVarargsArray() throws Throwable {
+ //System.out.println("varargsArray");
+ final int MIN = START_ARITY;
+ final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
+ for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) {
+ MethodHandle target = ValueConversions.varargsArray(nargs);
+ Object[] args = new Object[nargs];
+ for (int i = 0; i < nargs; i++)
+ args[i] = "#"+i;
+ Object res = target.invokeWithArguments(args);
+ assertArrayEquals(args, (Object[])res);
+ }
+ }
+
+ @Test
+ public void testVarargsReferenceArray() throws Throwable {
+ //System.out.println("varargsReferenceArray");
+ testTypedVarargsArray(Object[].class);
+ testTypedVarargsArray(String[].class);
+ testTypedVarargsArray(Number[].class);
+ }
+
+ @Test
+ public void testVarargsPrimitiveArray() throws Throwable {
+ //System.out.println("varargsPrimitiveArray");
+ testTypedVarargsArray(int[].class);
+ testTypedVarargsArray(long[].class);
+ testTypedVarargsArray(byte[].class);
+ testTypedVarargsArray(boolean[].class);
+ testTypedVarargsArray(short[].class);
+ testTypedVarargsArray(char[].class);
+ testTypedVarargsArray(float[].class);
+ testTypedVarargsArray(double[].class);
+ }
+
+ private static int nextArgCount(int nargs, int density, int MAX) {
+ if (EXHAUSTIVE) return nargs + 1;
+ if (nargs >= MAX) return Integer.MAX_VALUE;
+ int BOT = 20, TOP = MAX-5;
+ if (density < 10) { BOT = 10; MAX = TOP-2; }
+ if (nargs <= BOT || nargs >= TOP) {
+ ++nargs;
+ } else {
+ int bump = Math.max(1, 100 / density);
+ nargs += bump;
+ if (nargs > TOP) nargs = TOP;
+ }
+ return nargs;
+ }
+
+ private void testTypedVarargsArray(Class<?> arrayType) throws Throwable {
+ System.out.println(arrayType.getSimpleName());
+ Class<?> elemType = arrayType.getComponentType();
+ int MIN = START_ARITY;
+ int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
+ int density = 3;
+ if (elemType == int.class || elemType == long.class) density = 7;
+ if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; }
+ for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) {
+ Object[] args = makeTestArray(elemType, nargs);
+ MethodHandle varargsArray = ValueConversions.varargsArray(arrayType, nargs);
+ MethodType vaType = varargsArray.type();
+ assertEquals(arrayType, vaType.returnType());
+ if (nargs != 0) {
+ assertEquals(elemType, vaType.parameterType(0));
+ assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1));
+ }
+ assertEquals(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)),
+ vaType);
+ Object res = varargsArray.invokeWithArguments(args);
+ String resString = toArrayString(res);
+ assertEquals(Arrays.toString(args), resString);
+
+ MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs);
+ MethodType stype = spreader.type();
+ assert(stype == MethodType.methodType(arrayType, arrayType));
+ if (nargs <= 5) {
+ // invoke target as a spreader also:
+ Object res2 = spreader.invokeWithArguments((Object)res);
+ String res2String = toArrayString(res2);
+ assertEquals(Arrays.toString(args), res2String);
+ // invoke the spreader on a generic Object[] array; check for error
+ try {
+ Object res3 = spreader.invokeWithArguments((Object)args);
+ String res3String = toArrayString(res3);
+ assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
+ assertEquals(Arrays.toString(args), res3String);
+ } catch (ClassCastException ex) {
+ assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class));
+ }
+ }
+ if (nargs == 0) {
+ // invoke spreader on null arglist
+ Object res3 = spreader.invokeWithArguments((Object)null);
+ String res3String = toArrayString(res3);
+ assertEquals(Arrays.toString(args), res3String);
+ }
+ }
+ }
+
+ private static Object[] makeTestArray(Class<?> elemType, int len) {
+ Wrapper elem = null;
+ if (elemType.isPrimitive())
+ elem = Wrapper.forPrimitiveType(elemType);
+ else if (Wrapper.isWrapperType(elemType))
+ elem = Wrapper.forWrapperType(elemType);
+ Object[] args = new Object[len];
+ for (int i = 0; i < len; i++) {
+ Object arg = i * 100;
+ if (elem == null) {
+ if (elemType == String.class)
+ arg = "#"+arg;
+ arg = elemType.cast(arg); // just to make sure
+ } else {
+ switch (elem) {
+ case BOOLEAN: arg = (i % 3 == 0); break;
+ case CHAR: arg = 'a' + i; break;
+ case LONG: arg = (long)i * 1000_000_000; break;
+ case FLOAT: arg = (float)i / 100; break;
+ case DOUBLE: arg = (double)i / 1000_000; break;
+ }
+ arg = elem.cast(arg, elemType);
+ }
+ args[i] = arg;
+ }
+ //System.out.println(elemType.getName()+Arrays.toString(args));
+ return args;
+ }
+
+ private static String toArrayString(Object a) {
+ if (a == null) return "null";
+ Class<?> elemType = a.getClass().getComponentType();
+ if (elemType == null) return a.toString();
+ if (elemType.isPrimitive()) {
+ switch (Wrapper.forPrimitiveType(elemType)) {
+ case INT: return Arrays.toString((int[])a);
+ case BYTE: return Arrays.toString((byte[])a);
+ case BOOLEAN: return Arrays.toString((boolean[])a);
+ case SHORT: return Arrays.toString((short[])a);
+ case CHAR: return Arrays.toString((char[])a);
+ case FLOAT: return Arrays.toString((float[])a);
+ case LONG: return Arrays.toString((long[])a);
+ case DOUBLE: return Arrays.toString((double[])a);
+ }
+ }
+ return Arrays.toString((Object[])a);
+ }
+
+ @Test
+ public void testVarargsList() throws Throwable {
+ //System.out.println("varargsList");
+ final int MIN = START_ARITY;
+ final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added
+ for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 7, MAX)) {
+ MethodHandle target = ValueConversions.varargsList(nargs);
+ Object[] args = new Object[nargs];
+ for (int i = 0; i < nargs; i++)
+ args[i] = "#"+i;
+ Object res = target.invokeWithArguments(args);
+ assertEquals(Arrays.asList(args), res);
+ }
+ }
+}
--- a/langtools/.hgtags Tue May 24 14:15:14 2011 -0700
+++ b/langtools/.hgtags Tue May 24 14:32:10 2011 -0700
@@ -116,3 +116,4 @@
853b6bb99f9b58eb7cf8211c67d3b6e4f1228a3e jdk7-b139
258e6654aba25aab91c9ba3b4c53d05bc895a86c jdk7-b140
90adb5d6adc7d99d27c8b142a31ac8921070274f jdk7-b141
+7476b164194c1814704153e74d5ff7e965c6fdbf jdk7-b142
--- a/langtools/src/share/classes/com/sun/source/tree/SynchronizedTree.java Tue May 24 14:15:14 2011 -0700
+++ b/langtools/src/share/classes/com/sun/source/tree/SynchronizedTree.java Tue May 24 14:32:10 2011 -0700
@@ -31,7 +31,7 @@
* For example:
* <pre>
* synchronized ( <em>expression</em> )
- * </em>block</em>
+ * <em>block</em>
* </pre>
*
* @jls section 14.19
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java Tue May 24 14:15:14 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java Tue May 24 14:32:10 2011 -0700
@@ -1006,7 +1006,6 @@
try {
data = eval.call();
} catch (Exception ex) {
- ex.printStackTrace();
throw new AssertionError(ex);
}
}
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java Tue May 24 14:15:14 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Types.java Tue May 24 14:32:10 2011 -0700
@@ -955,7 +955,9 @@
if (t.isPrimitive() != s.isPrimitive())
return allowBoxing && (
isConvertible(t, s, warn)
- || (allowObjectToPrimitiveCast && isConvertible(s, t, warn)));
+ || (allowObjectToPrimitiveCast &&
+ s.isPrimitive() &&
+ isSubtype(boxedClass(s).type, t)));
if (warn != warnStack.head) {
try {
warnStack = warnStack.prepend(warn);
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java Tue May 24 14:15:14 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java Tue May 24 14:32:10 2011 -0700
@@ -1708,20 +1708,24 @@
// that we are referring to a superclass instance of the
// current instance (JLS ???).
else {
- localEnv.info.selectSuper = cdef != null;
- localEnv.info.varArgs = false;
+ //the following code alters some of the fields in the current
+ //AttrContext - hence, the current context must be dup'ed in
+ //order to avoid downstream failures
+ Env<AttrContext> rsEnv = localEnv.dup(tree);
+ rsEnv.info.selectSuper = cdef != null;
+ rsEnv.info.varArgs = false;
tree.constructor = rs.resolveConstructor(
- tree.pos(), localEnv, clazztype, argtypes, typeargtypes);
+ tree.pos(), rsEnv, clazztype, argtypes, typeargtypes);
tree.constructorType = tree.constructor.type.isErroneous() ?
syms.errType :
checkMethod(clazztype,
tree.constructor,
- localEnv,
+ rsEnv,
tree.args,
argtypes,
typeargtypes,
- localEnv.info.varArgs);
- if (localEnv.info.varArgs)
+ rsEnv.info.varArgs);
+ if (rsEnv.info.varArgs)
Assert.check(tree.constructorType.isErroneous() || tree.varargsElement != null);
}
@@ -1779,9 +1783,10 @@
// Reassign clazztype and recompute constructor.
clazztype = cdef.sym.type;
+ boolean useVarargs = tree.varargsElement != null;
Symbol sym = rs.resolveConstructor(
tree.pos(), localEnv, clazztype, argtypes,
- typeargtypes, true, tree.varargsElement != null);
+ typeargtypes, true, useVarargs);
Assert.check(sym.kind < AMBIGUOUS || tree.constructor.type.isErroneous());
tree.constructor = sym;
if (tree.constructor.kind > ERRONEOUS) {
@@ -1794,7 +1799,7 @@
tree.args,
argtypes,
typeargtypes,
- localEnv.info.varArgs);
+ useVarargs);
}
}
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java Tue May 24 14:15:14 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java Tue May 24 14:32:10 2011 -0700
@@ -407,9 +407,7 @@
// for varargs arguments as well
if (useVarargs) {
- //note: if applicability check is triggered by most specific test,
- //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5)
- Type elemType = types.elemtypeOrType(varargsFormal);
+ Type elemType = types.elemtype(varargsFormal);
Type elemUndet = types.subst(elemType, tvars, undetvars);
while (actuals.nonEmpty()) {
Type actual = actuals.head.baseType();
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java Tue May 24 14:15:14 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java Tue May 24 14:32:10 2011 -0700
@@ -458,9 +458,7 @@
throw inapplicableMethodException.setMessage("arg.length.mismatch"); // not enough args
if (useVarargs) {
- //note: if applicability check is triggered by most specific test,
- //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5)
- Type elt = types.elemtypeOrType(varargsFormal);
+ Type elt = types.elemtype(varargsFormal);
while (argtypes.nonEmpty()) {
if (!types.isConvertible(argtypes.head, elt, warn))
throw inapplicableMethodException.setMessage("varargs.argument.mismatch",
@@ -819,10 +817,10 @@
private boolean signatureMoreSpecific(Env<AttrContext> env, Type site, Symbol m1, Symbol m2, boolean allowBoxing, boolean useVarargs) {
noteWarner.clear();
Type mtype1 = types.memberType(site, adjustVarargs(m1, m2, useVarargs));
- return (instantiate(env, site, adjustVarargs(m2, m1, useVarargs), types.lowerBoundArgtypes(mtype1), null,
- allowBoxing, false, noteWarner) != null ||
- useVarargs && instantiate(env, site, adjustVarargs(m2, m1, useVarargs), types.lowerBoundArgtypes(mtype1), null,
- allowBoxing, true, noteWarner) != null) &&
+ Type mtype2 = instantiate(env, site, adjustVarargs(m2, m1, useVarargs),
+ types.lowerBoundArgtypes(mtype1), null,
+ allowBoxing, false, noteWarner);
+ return mtype2 != null &&
!noteWarner.hasLint(Lint.LintCategory.UNCHECKED);
}
//where
@@ -855,7 +853,7 @@
//append varargs element type as last synthetic formal
args.append(types.elemtype(varargsTypeTo));
Type mtype = types.createMethodTypeWithParameters(to.type, args.toList());
- return new MethodSymbol(to.flags_field, to.name, mtype, to.owner);
+ return new MethodSymbol(to.flags_field & ~VARARGS, to.name, mtype, to.owner);
} else {
return to;
}
--- a/langtools/test/tools/javac/types/BoxingConversionTest.java Tue May 24 14:15:14 2011 -0700
+++ b/langtools/test/tools/javac/types/BoxingConversionTest.java Tue May 24 14:32:10 2011 -0700
@@ -118,7 +118,6 @@
static final Result T = Result.OK_BOTH;
static final Result F = Result.FAIL_BOTH;
static final Result A = Result.OK_ASSIGN_ONLY;
- static final Result X = Result.FAIL_BOTH.FAIL_BOTH;
Result[][] results1 = {
//byte, short, int, long, float, double, char, bool, Byte, Short, Integer, Long, Float, Double, Character, Boolean
--- a/langtools/test/tools/javac/types/CastTest.java Tue May 24 14:15:14 2011 -0700
+++ b/langtools/test/tools/javac/types/CastTest.java Tue May 24 14:32:10 2011 -0700
@@ -42,12 +42,13 @@
*/
public class CastTest extends TypeHarness {
- Type[] allTypes;
+ Type[] types_no_boxing;
+ Type[] types_boxing;
static final boolean T = true;
static final boolean F = false;
- boolean[][] cast_result = {
+ boolean[][] cast_result_no_boxing = {
//byte, short, int, long, float, double, char, bool, C, +C, I, T, byte[], short[], int[], long[], float[], double[], char[], bool[], C[], +C[], I[], T[]
/*byte*/ { T , T , T , T , T , T , T , F , F, F , F, F, F , F , F , F , F , F , F , F , F , F , F , F },
/*short*/ { T , T , T , T , T , T , T , F , F, F , F, F, F , F , F , F , F , F , F , F , F , F , F , F },
@@ -74,6 +75,25 @@
/*I[]*/ { F , F , F , F , F , F , F , F , F, F , F, T, F , F , F , F , F , F , F , F , T , F , T , T },
/*T[]*/ { F , F , F , F , F , F , F , F , F, F , F, T, F , F , F , F , F , F , F , F , T , T , T , T }};
+ boolean[][] cast_result_boxing = {
+ //byte, short, int, long, float, double, char, bool, Byte, Short, Integer, Long, Float, Double, Character, Boolean, Object
+ /*byte*/ { T , T , T , T , T , T , T , F , T , F , F , F , F , F , F , F , T },
+ /*short*/ { T , T , T , T , T , T , T , F , F , T , F , F , F , F , F , F , T },
+ /*int*/ { T , T , T , T , T , T , T , F , F , F , T , F , F , F , F , F , T },
+ /*long*/ { T , T , T , T , T , T , T , F , F , F , F , T , F , F , F , F , T },
+ /*float*/ { T , T , T , T , T , T , T , F , F , F , F , F , T , F , F , F , T },
+ /*double*/ { T , T , T , T , T , T , T , F , F , F , F , F , F , T , F , F , T },
+ /*char*/ { T , T , T , T , T , T , T , F , F , F , F , F , F , F , T , F , T },
+ /*bool*/ { F , F , F , F , F , F , F , T , F , F , F , F , F , F , F , T , T },
+ /*Byte*/ { T , T , T , T , T , T , F , F , T , F , F , F , F , F , F , F , T },
+ /*Short*/ { F , T , T , T , T , T , F , F , F , T , F , F , F , F , F , F , T },
+ /*Integer*/ { F , F , T , T , T , T , F , F , F , F , T , F , F , F , F , F , T },
+ /*Long*/ { F , F , F , T , T , T , F , F , F , F , F , T , F , F , F , F , T },
+ /*Float*/ { F , F , F , F , T , T , F , F , F , F , F , F , T , F , F , F , T },
+ /*Double*/ { F , F , F , F , F , T , F , F , F , F , F , F , F , T , F , F , T },
+ /*Character*/ { F , F , T , T , T , T , T , F , F , F , F , F , F , F , T , F , T },
+ /*Boolean*/ { F , F , F , F , F , F , F , T , F , F , F , F , F , F , F , T , T },
+ /*Object*/ { T , T , T , T , T , T , T , T , T , T , T , T , T , T , T , T , T }};
CastTest() {
Type[] primitiveTypes = {
predef.byteType,
@@ -85,6 +105,15 @@
predef.charType,
predef.booleanType };
+ Type[] boxedTypes = new Type[primitiveTypes.length + 1];
+ for (int i = 0 ; i < primitiveTypes.length ; i++) {
+ boxedTypes[i] = box(primitiveTypes[i]);
+ }
+
+ boxedTypes[primitiveTypes.length] = predef.objectType;
+
+ types_boxing = join(Type.class, primitiveTypes, boxedTypes);
+
Type[] referenceTypes = {
fac.Class(),
fac.Class(FINAL),
@@ -97,17 +126,22 @@
arrayTypes[idx++] = fac.Array(t);
}
- allTypes = join(Type.class, primitiveTypes, referenceTypes, arrayTypes);
+ types_no_boxing = join(Type.class, primitiveTypes, referenceTypes, arrayTypes);
}
- void test() {
- for (int i = 0; i < allTypes.length ; i++) {
- for (int j = 0; j < allTypes.length ; j++) {
- assertCastable(allTypes[i], allTypes[j], cast_result[i][j]);
+ void test(Type[] all_types, boolean[][] cast_result) {
+ for (int i = 0; i < all_types.length ; i++) {
+ for (int j = 0; j < all_types.length ; j++) {
+ assertCastable(all_types[i], all_types[j], cast_result[i][j]);
}
}
}
+ void runTests() {
+ test(types_no_boxing, cast_result_no_boxing);
+ test(types_boxing, cast_result_boxing);
+ }
+
@SuppressWarnings("unchecked")
<T> T[] join(Class<T> type, T[]... args) {
int totalLength = 0;
@@ -124,6 +158,6 @@
}
public static void main(String[] args) {
- new CastTest().test();
+ new CastTest().runTests();
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/varargs/7042566/T7042566.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7042566
+ * @summary Unambiguous varargs method calls flagged as ambiguous
+ */
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.classfile.Instruction;
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.Code_attribute;
+import com.sun.tools.classfile.ConstantPool.*;
+import com.sun.tools.classfile.Method;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.util.List;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Locale;
+import javax.tools.Diagnostic;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+public class T7042566 {
+
+ VarargsMethod m1;
+ VarargsMethod m2;
+ TypeConfiguration actuals;
+
+ T7042566(TypeConfiguration m1_conf, TypeConfiguration m2_conf, TypeConfiguration actuals) {
+ this.m1 = new VarargsMethod(m1_conf);
+ this.m2 = new VarargsMethod(m2_conf);
+ this.actuals = actuals;
+ }
+
+ void compileAndCheck() throws Exception {
+ final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+ JavaSource source = new JavaSource();
+ ErrorChecker ec = new ErrorChecker();
+ JavacTask ct = (JavacTask)tool.getTask(null, fm, ec,
+ null, null, Arrays.asList(source));
+ ct.call();
+ check(source, ec);
+ }
+
+ void check(JavaSource source, ErrorChecker ec) {
+ checkCount++;
+ boolean resolutionError = false;
+ VarargsMethod selectedMethod = null;
+
+ boolean m1_applicable = m1.isApplicable(actuals);
+ boolean m2_applicable = m2.isApplicable(actuals);
+
+ if (!m1_applicable && !m2_applicable) {
+ resolutionError = true;
+ } else if (m1_applicable && m2_applicable) {
+ //most specific
+ boolean m1_moreSpecific = m1.isMoreSpecificThan(m2);
+ boolean m2_moreSpecific = m2.isMoreSpecificThan(m1);
+
+ resolutionError = m1_moreSpecific == m2_moreSpecific;
+ selectedMethod = m1_moreSpecific ? m1 : m2;
+ } else {
+ selectedMethod = m1_applicable ?
+ m1 : m2;
+ }
+
+ if (ec.errorFound != resolutionError) {
+ throw new Error("invalid diagnostics for source:\n" +
+ source.getCharContent(true) +
+ "\nExpected resolution error: " + resolutionError +
+ "\nFound error: " + ec.errorFound +
+ "\nCompiler diagnostics:\n" + ec.printDiags());
+ } else if (!resolutionError) {
+ verifyBytecode(selectedMethod, source);
+ }
+ }
+
+ void verifyBytecode(VarargsMethod selected, JavaSource source) {
+ bytecodeCheckCount++;
+ File compiledTest = new File("Test.class");
+ try {
+ ClassFile cf = ClassFile.read(compiledTest);
+ Method testMethod = null;
+ for (Method m : cf.methods) {
+ if (m.getName(cf.constant_pool).equals("test")) {
+ testMethod = m;
+ break;
+ }
+ }
+ if (testMethod == null) {
+ throw new Error("Test method not found");
+ }
+ Code_attribute ea = (Code_attribute)testMethod.attributes.get(Attribute.Code);
+ if (testMethod == null) {
+ throw new Error("Code attribute for test() method not found");
+ }
+
+ for (Instruction i : ea.getInstructions()) {
+ if (i.getMnemonic().equals("invokevirtual")) {
+ int cp_entry = i.getUnsignedShort(1);
+ CONSTANT_Methodref_info methRef =
+ (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry);
+ String type = methRef.getNameAndTypeInfo().getType();
+ String sig = selected.parameterTypes.bytecodeSigStr;
+ if (!type.contains(sig)) {
+ throw new Error("Unexpected type method call: " + type + "" +
+ "\nfound: " + sig +
+ "\n" + source.getCharContent(true));
+ }
+ break;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new Error("error reading " + compiledTest +": " + e);
+ }
+ }
+
+ class JavaSource extends SimpleJavaFileObject {
+
+ static final String source_template = "class Test {\n" +
+ " #V1\n" +
+ " #V2\n" +
+ " void test() { m(#E); }\n" +
+ "}";
+
+ String source;
+
+ public JavaSource() {
+ super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+ source = source_template.replaceAll("#V1", m1.toString()).
+ replaceAll("#V2", m2.toString()).
+ replaceAll("#E", actuals.expressionListStr);
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return source;
+ }
+ }
+
+ /** global decls ***/
+
+ // Create a single file manager and reuse it for each compile to save time.
+ static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
+
+ //statistics
+ static int checkCount = 0;
+ static int bytecodeCheckCount = 0;
+
+ public static void main(String... args) throws Exception {
+ for (TypeConfiguration tconf1 : TypeConfiguration.values()) {
+ for (TypeConfiguration tconf2 : TypeConfiguration.values()) {
+ for (TypeConfiguration tconf3 : TypeConfiguration.values()) {
+ new T7042566(tconf1, tconf2, tconf3).compileAndCheck();
+ }
+ }
+ }
+
+ System.out.println("Total checks made: " + checkCount);
+ System.out.println("Bytecode checks made: " + bytecodeCheckCount);
+ }
+
+ enum TypeKind {
+ OBJECT("Object", "(Object)null", "Ljava/lang/Object;"),
+ STRING("String", "(String)null", "Ljava/lang/String;");
+
+ String typeString;
+ String valueString;
+ String bytecodeString;
+
+ TypeKind(String typeString, String valueString, String bytecodeString) {
+ this.typeString = typeString;
+ this.valueString = valueString;
+ this.bytecodeString = bytecodeString;
+ }
+
+ boolean isSubtypeOf(TypeKind that) {
+ return that == OBJECT ||
+ (that == STRING && this == STRING);
+ }
+ }
+
+ enum TypeConfiguration {
+ A(TypeKind.OBJECT),
+ B(TypeKind.STRING),
+ AA(TypeKind.OBJECT, TypeKind.OBJECT),
+ AB(TypeKind.OBJECT, TypeKind.STRING),
+ BA(TypeKind.STRING, TypeKind.OBJECT),
+ BB(TypeKind.STRING, TypeKind.STRING),
+ AAA(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.OBJECT),
+ AAB(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.STRING),
+ ABA(TypeKind.OBJECT, TypeKind.STRING, TypeKind.OBJECT),
+ ABB(TypeKind.OBJECT, TypeKind.STRING, TypeKind.STRING),
+ BAA(TypeKind.STRING, TypeKind.OBJECT, TypeKind.OBJECT),
+ BAB(TypeKind.STRING, TypeKind.OBJECT, TypeKind.STRING),
+ BBA(TypeKind.STRING, TypeKind.STRING, TypeKind.OBJECT),
+ BBB(TypeKind.STRING, TypeKind.STRING, TypeKind.STRING);
+
+ List<TypeKind> typeKindList;
+ String expressionListStr;
+ String parameterListStr;
+ String bytecodeSigStr;
+
+ private TypeConfiguration(TypeKind... typeKindList) {
+ this.typeKindList = List.from(typeKindList);
+ expressionListStr = asExpressionList();
+ parameterListStr = asParameterList();
+ bytecodeSigStr = asBytecodeString();
+ }
+
+ private String asExpressionList() {
+ StringBuilder buf = new StringBuilder();
+ String sep = "";
+ for (TypeKind tk : typeKindList) {
+ buf.append(sep);
+ buf.append(tk.valueString);
+ sep = ",";
+ }
+ return buf.toString();
+ }
+
+ private String asParameterList() {
+ StringBuilder buf = new StringBuilder();
+ String sep = "";
+ int count = 0;
+ for (TypeKind arg : typeKindList) {
+ buf.append(sep);
+ buf.append(arg.typeString);
+ if (count == (typeKindList.size() - 1)) {
+ buf.append("...");
+ }
+ buf.append(" ");
+ buf.append("arg" + count++);
+ sep = ",";
+ }
+ return buf.toString();
+ }
+
+ private String asBytecodeString() {
+ StringBuilder buf = new StringBuilder();
+ int count = 0;
+ for (TypeKind arg : typeKindList) {
+ if (count == (typeKindList.size() - 1)) {
+ buf.append("[");
+ }
+ buf.append(arg.bytecodeString);
+ count++;
+ }
+ return buf.toString();
+ }
+ }
+
+ static class VarargsMethod {
+ TypeConfiguration parameterTypes;
+
+ public VarargsMethod(TypeConfiguration parameterTypes) {
+ this.parameterTypes = parameterTypes;
+ }
+
+ @Override
+ public String toString() {
+ return "void m( " + parameterTypes.parameterListStr + ") {}";
+ }
+
+ boolean isApplicable(TypeConfiguration that) {
+ List<TypeKind> actuals = that.typeKindList;
+ List<TypeKind> formals = parameterTypes.typeKindList;
+ if ((actuals.size() - formals.size()) < -1)
+ return false; //not enough args
+ for (TypeKind actual : actuals) {
+ if (!actual.isSubtypeOf(formals.head))
+ return false; //type mismatch
+ formals = formals.tail.isEmpty() ?
+ formals :
+ formals.tail;
+ }
+ return true;
+ }
+
+ boolean isMoreSpecificThan(VarargsMethod that) {
+ List<TypeKind> actuals = parameterTypes.typeKindList;
+ List<TypeKind> formals = that.parameterTypes.typeKindList;
+ int checks = 0;
+ int expectedCheck = Math.max(actuals.size(), formals.size());
+ while (checks < expectedCheck) {
+ if (!actuals.head.isSubtypeOf(formals.head))
+ return false; //type mismatch
+ formals = formals.tail.isEmpty() ?
+ formals :
+ formals.tail;
+ actuals = actuals.tail.isEmpty() ?
+ actuals :
+ actuals.tail;
+ checks++;
+ }
+ return true;
+ }
+ }
+
+ static class ErrorChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+ boolean errorFound;
+ List<String> errDiags = List.nil();
+
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+ errDiags = errDiags.append(diagnostic.getMessage(Locale.getDefault()));
+ errorFound = true;
+ }
+ }
+
+ String printDiags() {
+ StringBuilder buf = new StringBuilder();
+ for (String s : errDiags) {
+ buf.append(s);
+ buf.append("\n");
+ }
+ return buf.toString();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/varargs/7043922/T7043922.java Tue May 24 14:32:10 2011 -0700
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7043922
+ * @summary Regression: internal compiler error for nested anonymous inner class featuring varargs constructor
+ */
+
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.JavacTool;
+import com.sun.tools.javac.util.List;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Locale;
+import javax.tools.Diagnostic;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+public class T7043922 {
+
+ ClassKind[] classKinds;
+ ConstructorKind[] constructorKinds;
+
+ T7043922(ClassKind[] classKinds, ConstructorKind[] constructorKinds) {
+ this.classKinds = classKinds;
+ this.constructorKinds = constructorKinds;
+ }
+
+ void compileAndCheck() throws Exception {
+ JavaSource source = new JavaSource();
+ ErrorChecker ec = new ErrorChecker();
+ JavacTask ct = (JavacTask)tool.getTask(null, fm, ec,
+ null, null, Arrays.asList(source));
+ ct.analyze();
+ if (ec.errorFound) {
+ throw new Error("invalid diagnostics for source:\n" +
+ source.getCharContent(true) +
+ "\nCompiler diagnostics:\n" + ec.printDiags());
+ }
+ }
+
+ class JavaSource extends SimpleJavaFileObject {
+
+ static final String source_template = "#C0 A0 { #K0 }\n" +
+ "#C1 A1 { #K1 }\n" +
+ "#C2 A2 { #K2 }\n" +
+ "class D {\n" +
+ " void test() {\n" +
+ " new A0(#V0) {\n" +
+ " void test() {\n" +
+ " new A1(#V1) {\n" +
+ " void test() {\n" +
+ " new A2(#V2) {};\n" +
+ " }\n" +
+ " };\n" +
+ " }\n" +
+ " };\n" +
+ " }\n" +
+ "}\n";
+
+ String source;
+
+ public JavaSource() {
+ super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+ source = source_template;
+ for (int i = 0; i < 3; i++) {
+ source = source.replaceAll("#C" + i, classKinds[i].classKind).
+ replaceAll("#K" + i, classKinds[i].getConstructor("A" + i, constructorKinds[i])).
+ replaceAll("#V" + i, constructorKinds[i].constrArgs);
+ }
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return source;
+ }
+ }
+
+ /** global decls ***/
+
+ enum ConstructorKind {
+ DEFAULT("", ""),
+ FIXED_ARITY("String s", "\"\""),
+ VARIABLE_ARITY("String... ss", "\"\",\"\"");
+
+ String constrParam;
+ String constrArgs;
+
+ private ConstructorKind(String constrParam, String constrArgs) {
+ this.constrParam = constrParam;
+ this.constrArgs = constrArgs;
+ }
+ }
+
+ enum ClassKind {
+ ABSTRACT("abstract class"),
+ CLASS("class"),
+ INTERFACE("interface");
+
+ String classKind;
+
+ private ClassKind(String classKind) {
+ this.classKind = classKind;
+ }
+
+ boolean isConstructorOk(ConstructorKind ck) {
+ return this != INTERFACE ||
+ ck == ConstructorKind.DEFAULT;
+ }
+
+ String getConstructor(String className, ConstructorKind ck) {
+ return this == INTERFACE ?
+ "" :
+ (className + "(" + ck.constrParam + ") {}");
+ }
+ }
+
+ // Create a single file manager and JavaCompiler tool
+ // and reuse them for each compile to save time.
+ static final StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
+ static final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+
+ public static void main(String... args) throws Exception {
+ for (ClassKind classKind1 : ClassKind.values()) {
+ for (ConstructorKind constrKind1 : ConstructorKind.values()) {
+ if (!classKind1.isConstructorOk(constrKind1)) continue;
+ for (ClassKind classKind2 : ClassKind.values()) {
+ for (ConstructorKind constrKind2 : ConstructorKind.values()) {
+ if (!classKind2.isConstructorOk(constrKind2)) continue;
+ for (ClassKind classKind3 : ClassKind.values()) {
+ for (ConstructorKind constrKind3 : ConstructorKind.values()) {
+ if (!classKind3.isConstructorOk(constrKind3)) continue;
+ new T7043922(new ClassKind[] { classKind1, classKind2, classKind3 },
+ new ConstructorKind[] { constrKind1, constrKind2, constrKind3 }).compileAndCheck();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ static class ErrorChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
+
+ boolean errorFound;
+ List<String> errDiags = List.nil();
+
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
+ errDiags = errDiags.append(diagnostic.getMessage(Locale.getDefault()));
+ errorFound = true;
+ }
+ }
+
+ String printDiags() {
+ StringBuilder buf = new StringBuilder();
+ for (String s : errDiags) {
+ buf.append(s);
+ buf.append("\n");
+ }
+ return buf.toString();
+ }
+ }
+}
--- a/make/jprt.gmk Tue May 24 14:15:14 2011 -0700
+++ b/make/jprt.gmk Tue May 24 14:32:10 2011 -0700
@@ -37,7 +37,7 @@
JPRT_ARCHIVE_INSTALL_BUNDLE=$(ABS_OUTPUTDIR)/$(DEFAULT_BUILD_FLAVOR)-install-bundle.zip
jprt_build_product: sanity all_product_build
- ( $(CD) $(OUTPUTDIR)/j2sdk-image && \
+ ( $(CD) $(OUTPUTDIR)/$(JDK_IMAGE_DIRNAME) && \
$(ZIPEXE) -q -r $(JPRT_ARCHIVE_BUNDLE) . )
ifdef HAVE_JPRT_SAVE_BUNDLES
( $(CD) $(OUTPUTDIR)/bundles && \
@@ -45,11 +45,11 @@
endif
jprt_build_fastdebug: fastdebug_build
- ( $(CD) $(OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-fastdebug/j2sdk-image && \
+ ( $(CD) $(OUTPUTDIR)/$(REL_JDK_FASTDEBUG_IMAGE_DIR) && \
$(ZIPEXE) -q -r $(JPRT_ARCHIVE_BUNDLE) . )
jprt_build_debug: debug_build
- ( $(CD) $(OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-debug/j2sdk-image && \
+ ( $(CD) $(OUTPUTDIR)/$(REL_JDK_DEBUG_IMAGE_DIR) && \
$(ZIPEXE) -q -r $(JPRT_ARCHIVE_BUNDLE) . )
################################################################